简体   繁体   中英

Django filter traversing multiple foreign keys and generic foreign keys

I've tried a ton of different ways, but I've hit a brick wall with the logic and hoping someone on here may be able to come up with the easily.

I have a few different connected relevant models (I've tried to strip out all irrelevant fields and methods for ease of viewing):

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")

I'm trying to filter for InventoryItem's based on user-inputs from html form (example below)

post_dict = {'style_id': 'CQ4227-030', 'size': '8', 'condition': 'NWB', 'warehouse': 'A',
             'location': 'G', 'sublocation': '2'}

It seems like there are two sets of problems. The first being how to reverse generic relationships and foreign keys for their attributes (when looking for style_id, size, condition) and the second being how to filter based on a method outcome (InventoryItem.current_location() method for warehouse, location, sublocation)

I've tried a bunch of different methods (querysets, managers, list comprehension), but just cant wrap my head around an efficient working method. If anyone can come up with a way to filter for all the above easily, it would be greatly appreciated (filter would be AND, not OR)

Your relations are a bit difficult to understand, but below is a simple example of how to use querysets with GenericForeignKeys and 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')

Then we can do this:

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)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM