Django过滤器遍历多个外键和通用外键
Posted
技术标签:
【中文标题】Django过滤器遍历多个外键和通用外键【英文标题】:Django filter traversing multiple foreign keys and generic foreign keys 【发布时间】:2021-07-27 03:13:21 【问题描述】:我尝试了很多不同的方法,但我在逻辑上碰壁了,希望这里的人能够轻松提出。
我有几个不同的连接相关模型(为了便于查看,我已尝试去除所有不相关的字段和方法):
class InventoryAddress:
def __init__(self, warehouse, location, sublocation):
self.warehouse = warehouse
self.location = location
self.sublocation = sublocation
# Product Models
class ProductMaster(models.Model):
internal_id = models.CharField(max_length=20, unique=True, primary_key=True, null=False, blank=False,
verbose_name="Internal ID")
class ShoeAttributes(models.Model):
style_id = models.CharField(max_length=20, unique=True, primary_key=True, null=False, blank=False,
verbose_name="Style ID")
product_master = models.OneToOneField(ProductMaster, related_name='ShoeAttributes', on_delete=models.CASCADE)
brand = models.CharField(max_length=30, choices=shoe_brand_choices, default=None, blank=True, null=True,
verbose_name="Brand")
class Shoe(models.Model):
sku = models.CharField(max_length=20, unique=True, primary_key=True, null=False, blank=False,
verbose_name="SKU")
product_master = models.ForeignKey(ProductMaster, blank=False, null=False, on_delete=models.CASCADE, verbose_name="Brands")
size = models.CharField(max_length=20, null=False, blank=False, verbose_name="Size")
condition = models.CharField(max_length=25, choices=shoe_condition_choices, blank=False, null=False,
verbose_name='Condition')
class InventoryItem(models.Model):
inventory_sku = models.CharField(max_length=20, unique=True, primary_key=True, null=False, blank=False,
verbose_name="Inventory SKU")
authenticated = models.CharField(max_length=2, choices=yes_no_choices, verbose_name='Authentication Status')
# GenericForeignKey for Specific Product
content_type = models.ForeignKey(ContentType, on_delete=models.PROTECT, null=True)
object_id = models.CharField(max_length=50)
content_object = GenericForeignKey('content_type', 'object_id')
def movements(self):
movements = InventoryMovement.objects.filter(inventory_item=self)
return sorted(movements, key=lambda x: x.datetime_entered, reverse=True)
def completed_movements(self):
movements = InventoryMovement.objects.filter(inventory_item=self, datetime_completed__isnull=False)
return sorted(movements, key=lambda x: x.datetime_completed, reverse=True)
def current_location(self):
movements = self.completed_movements()
if movements:
last_movement = movements[0]
loc = InventoryAddress(
warehouse=last_movement.warehouse,
location=last_movement.location,
sublocation=last_movement.sublocation
)
else:
loc = InventoryAddress(
warehouse='EXT',
location=None,
sublocation=None,
)
return loc
def current_location_display(self):
loc = self.current_location()
setattr(loc, 'warehouse', reverse_warehouse_location_vars[self.current_location().warehouse].title())
return loc
class InventoryMovement(models.Model):
movement_id = models.CharField(max_length=20, unique=True, blank=False, null=False, verbose_name='Movement ID')
warehouse = models.CharField(max_length=40, null=False, blank=False, choices=warehouse_location_choices,
verbose_name='Warehouse')
location = models.CharField(max_length=40, null=False, blank=False, verbose_name='Target Bin')
sublocation = models.CharField(max_length=40, null=False, blank=False, verbose_name='Target Position')
inventory_item = models.ForeignKey(InventoryItem, null=True, on_delete=models.PROTECT,
verbose_name="Inventory Item")
我正在尝试根据来自 html 表单的用户输入过滤 InventoryItem(示例如下)
post_dict = 'style_id': 'CQ4227-030', 'size': '8', 'condition': 'NWB', 'warehouse': 'A',
'location': 'G', 'sublocation': '2'
似乎有两组问题。第一个是如何反转其属性的通用关系和外键(在查找 style_id、大小、条件时),第二个是如何根据方法结果进行过滤(针对仓库、位置、子位置的 InventoryItem.current_location() 方法)
我尝试了很多不同的方法(查询集、管理器、列表理解),但就是无法找到一种有效的工作方法。如果有人能想出一种方法来轻松过滤上述所有内容,将不胜感激(过滤器将是 AND,而不是 OR)
【问题讨论】:
【参考方案1】:您的关系有点难以理解,但下面是一个简单的示例,说明如何将查询集与GenericForeignKeys
和GenericRelations
一起使用:
models.py
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType
class Order(models.Model):
''' generic order class '''
# fields:
price = models.DecimcalField(...)
# generic foreign key to a product:
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True)
object_id = models.PositiveIntegerField(null=True)
product = GenericForeignKey('content_type', 'object_id')
class Shirt(models.Model):
''' shirt class representing shirts available for sale '''
# fields:
color = models.CharField(...)
# generic relation:
order = GenericRelation('Order', related_query_name='shirt')
class Shoe(models.Model):
''' shoe class representing shoes available for sale '''
# fields:
size = models.CharField(...)
# generic relation:
order = GenericRelation('Order', related_query_name='shoe')
那么我们可以这样做:
views.py
# create a new order with a shirt:
blue_shirt = Shirt.objects.get(color='blue')
order = Order.objects.create(price=10.00, product=blue_shirt)
# lookup orders with a given shirt:
orders_with_blue_shirt = Order.objects.filter(shirt=blue_shirt)
# lookup orders with a given shoe:
orders_with_large_shoe = Order.objects.filter(shoe__size='large')
# find all shirts that were on orders with a price larger than 10.00:
shirts_over_10 = Shirt.objects.filter(order__price__gt = 10.00)
【讨论】:
以上是关于Django过滤器遍历多个外键和通用外键的主要内容,如果未能解决你的问题,请参考以下文章