mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Refs #11964 -- Made Q objects deconstructible.
This commit is contained in:
		| @@ -56,7 +56,9 @@ class Q(tree.Node): | ||||
|     default = AND | ||||
|  | ||||
|     def __init__(self, *args, **kwargs): | ||||
|         super().__init__(children=list(args) + list(kwargs.items())) | ||||
|         connector = kwargs.pop('_connector', None) | ||||
|         negated = kwargs.pop('_negated', False) | ||||
|         super().__init__(children=list(args) + list(kwargs.items()), connector=connector, negated=negated) | ||||
|  | ||||
|     def _combine(self, other, conn): | ||||
|         if not isinstance(other, Q): | ||||
| @@ -86,6 +88,19 @@ class Q(tree.Node): | ||||
|         query.promote_joins(joins) | ||||
|         return clause | ||||
|  | ||||
|     def deconstruct(self): | ||||
|         path = '%s.%s' % (self.__class__.__module__, self.__class__.__name__) | ||||
|         args, kwargs = (), {} | ||||
|         if len(self.children) == 1 and not isinstance(self.children[0], Q): | ||||
|             child = self.children[0] | ||||
|             kwargs = {child[0]: child[1]} | ||||
|         else: | ||||
|             args = tuple(self.children) | ||||
|             kwargs = {'_connector': self.connector} | ||||
|         if self.negated: | ||||
|             kwargs['_negated'] = True | ||||
|         return path, args, kwargs | ||||
|  | ||||
|  | ||||
| class DeferredAttribute: | ||||
|     """ | ||||
|   | ||||
| @@ -63,6 +63,13 @@ class Node: | ||||
|         """Return True if 'other' is a direct child of this instance.""" | ||||
|         return other in self.children | ||||
|  | ||||
|     def __eq__(self, other): | ||||
|         if self.__class__ != other.__class__: | ||||
|             return False | ||||
|         if (self.connector, self.negated) == (other.connector, other.negated): | ||||
|             return self.children == other.children | ||||
|         return False | ||||
|  | ||||
|     def add(self, data, conn_type, squash=True): | ||||
|         """ | ||||
|         Combine this tree and the data represented by data using the | ||||
|   | ||||
							
								
								
									
										76
									
								
								tests/queries/test_q.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								tests/queries/test_q.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | ||||
| from django.db.models import F, Q | ||||
| from django.test import SimpleTestCase | ||||
|  | ||||
|  | ||||
| class QTests(SimpleTestCase): | ||||
|     def test_deconstruct(self): | ||||
|         q = Q(price__gt=F('discounted_price')) | ||||
|         path, args, kwargs = q.deconstruct() | ||||
|         self.assertEqual(path, 'django.db.models.query_utils.Q') | ||||
|         self.assertEqual(args, ()) | ||||
|         self.assertEqual(kwargs, {'price__gt': F('discounted_price')}) | ||||
|  | ||||
|     def test_deconstruct_negated(self): | ||||
|         q = ~Q(price__gt=F('discounted_price')) | ||||
|         path, args, kwargs = q.deconstruct() | ||||
|         self.assertEqual(path, 'django.db.models.query_utils.Q') | ||||
|         self.assertEqual(args, ()) | ||||
|         self.assertEqual(kwargs, { | ||||
|             'price__gt': F('discounted_price'), | ||||
|             '_negated': True, | ||||
|         }) | ||||
|  | ||||
|     def test_deconstruct_or(self): | ||||
|         q1 = Q(price__gt=F('discounted_price')) | ||||
|         q2 = Q(price=F('discounted_price')) | ||||
|         q = q1 | q2 | ||||
|         path, args, kwargs = q.deconstruct() | ||||
|         self.assertEqual(path, 'django.db.models.query_utils.Q') | ||||
|         self.assertEqual(args, ( | ||||
|             ('price__gt', F('discounted_price')), | ||||
|             ('price', F('discounted_price')), | ||||
|         )) | ||||
|         self.assertEqual(kwargs, {'_connector': 'OR'}) | ||||
|  | ||||
|     def test_deconstruct_and(self): | ||||
|         q1 = Q(price__gt=F('discounted_price')) | ||||
|         q2 = Q(price=F('discounted_price')) | ||||
|         q = q1 & q2 | ||||
|         path, args, kwargs = q.deconstruct() | ||||
|         self.assertEqual(path, 'django.db.models.query_utils.Q') | ||||
|         self.assertEqual(args, ( | ||||
|             ('price__gt', F('discounted_price')), | ||||
|             ('price', F('discounted_price')), | ||||
|         )) | ||||
|         self.assertEqual(kwargs, {'_connector': 'AND'}) | ||||
|  | ||||
|     def test_deconstruct_nested(self): | ||||
|         q = Q(Q(price__gt=F('discounted_price'))) | ||||
|         path, args, kwargs = q.deconstruct() | ||||
|         self.assertEqual(path, 'django.db.models.query_utils.Q') | ||||
|         self.assertEqual(args, (Q(price__gt=F('discounted_price')),)) | ||||
|         self.assertEqual(kwargs, {'_connector': 'AND'}) | ||||
|  | ||||
|     def test_reconstruct(self): | ||||
|         q = Q(price__gt=F('discounted_price')) | ||||
|         path, args, kwargs = q.deconstruct() | ||||
|         self.assertEqual(Q(*args, **kwargs), q) | ||||
|  | ||||
|     def test_reconstruct_negated(self): | ||||
|         q = ~Q(price__gt=F('discounted_price')) | ||||
|         path, args, kwargs = q.deconstruct() | ||||
|         self.assertEqual(Q(*args, **kwargs), q) | ||||
|  | ||||
|     def test_reconstruct_or(self): | ||||
|         q1 = Q(price__gt=F('discounted_price')) | ||||
|         q2 = Q(price=F('discounted_price')) | ||||
|         q = q1 | q2 | ||||
|         path, args, kwargs = q.deconstruct() | ||||
|         self.assertEqual(Q(*args, **kwargs), q) | ||||
|  | ||||
|     def test_reconstruct_and(self): | ||||
|         q1 = Q(price__gt=F('discounted_price')) | ||||
|         q2 = Q(price=F('discounted_price')) | ||||
|         q = q1 & q2 | ||||
|         path, args, kwargs = q.deconstruct() | ||||
|         self.assertEqual(Q(*args, **kwargs), q) | ||||
| @@ -55,3 +55,19 @@ class NodeTests(unittest.TestCase): | ||||
|         node5 = copy.deepcopy(self.node1) | ||||
|         self.assertIs(self.node1.children, node4.children) | ||||
|         self.assertIsNot(self.node1.children, node5.children) | ||||
|  | ||||
|     def test_eq_children(self): | ||||
|         node = Node(self.node1_children) | ||||
|         self.assertEqual(node, self.node1) | ||||
|         self.assertNotEqual(node, self.node2) | ||||
|  | ||||
|     def test_eq_connector(self): | ||||
|         new_node = Node(connector='NEW') | ||||
|         default_node = Node(connector='DEFAULT') | ||||
|         self.assertEqual(default_node, self.node2) | ||||
|         self.assertNotEqual(default_node, new_node) | ||||
|  | ||||
|     def test_eq_negated(self): | ||||
|         node = Node(negated=False) | ||||
|         negated = Node(negated=True) | ||||
|         self.assertNotEqual(negated, node) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user