Django when is concrete inheritance appropriate - python

Let me start by stating that I have looked at django-polymorphic for this and still have questions. I have item models and many subtypes for items. Currently, my models look like this.
class Item(models.Model):
pass
class Television(Item):
upc = models.CharField(max_length=12, null=True)
title = models.CharField(max_length=255, null=True)
brand = models.ForeignKey(Brand)
screen_size = models.IntegerField()
class Fridge(Item):
upc = models.CharField(max_length=12, null=True)
title = models.CharField(max_length=255, null=True)
brand = models.ForeignKey(Brand)
stuff_about_fridge = models.CharField(max_length=255, null=True)
I did this at first because then I wouldn't worry about all of the left joins when querying different item types that would be caused if my models looked like this:
class Item(models.Model):
upc = models.CharField(max_length=12, null=True)
title = models.CharField(max_length=255, null=True)
brand = models.ForeignKey(Brand)
class Television(Item):
screen_size = models.IntegerField()
class Fridge(Item):
stuff_about_fridge = models.CharField(max_length=255, null=True)
I am now reaching a point where I realize that I very often query all of the Item models together and have to left join information from the subtypes instead, so I am not really saving myself there. My question is, even if I used something like django-polymorphic, would it make sense to A) put everything that is shared in the parent model and just specific things in the child models or to B) have it like I do where everything is in the child model, but they share a parent model PK just so that they can be queried together?

"Abstract base classes are useful when you want to put some common information into a number of other models. This model will then not be used to create any database table. Instead, when it is used as a base class for other models, its fields will be added to those of the child class"
Reference: https://docs.djangoproject.com/en/3.0/topics/db/models/#abstract-base-classes
The point of using inheritance is for sharing common info as mentioned in option (A).
Django-polymorphic makes using inherited models easier, nothing more.
Reference: https://django-polymorphic.readthedocs.io/en/stable/
That means, in conclusion, option (A) is correct even though if you use Django-polymorphic.
Below method is the right approach:
class Item(models.Model):
upc = models.CharField(max_length=12, null=True)
title = models.CharField(max_length=255, null=True)
brand = models.ForeignKey(Brand)
class Television(Item):
screen_size = models.IntegerField()
class Fridge(Item):
stuff_about_fridge = models.CharField(max_length=255, null=True)

Related

How can i perform the right reverse query in ManytoMany in Django

I'm trying to perform a reversed query for a manytomany fields in Django, but it keeps gives me nothing, here is my code
models.py
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=120)
image = models.ImageField(upload_to='products')
branch = models.ManyToManyField(Branch, related_name='branches')
class Branch(models.Model):
area = models.ForeignKey(Area, on_delete=CASCADE)
name = models.CharField(max_length=1200)
phone = models.CharField(max_length=30, null=True, blank=True)
address = models.CharField(max_length=1200, null=True, blank=True)
tax_value = models.DecimalField(decimal_places=2, max_digits=4)
views.py
for branch in product_object.branches.all():
print(branch)
The branch is always nothing !!
For some reason, the related name is not calling it anymore. I called it using the model name (lower cased).
This is how it worked
for branch in product_object.branch.all():
Just to complete your answer above, I think the way you have your model set up is a little misleading and confusing. I think this would improve clarity:
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=120)
image = models.ImageField(upload_to='products')
branches = models.ManyToManyField(Branch, related_name='products')
Since you have a many to many field, a product can have multiple branches, the attribute name should reflect that
When you use the related_name, this would be if you are going from the m2m object. For example, if you have a branch, you could get all it's products by doing branch.products

Using Model.objects.all() as a blueprint for a secondary table entry

I am having a bit of trouble with the logic of how this should work so I am hoping it is possible.
I figured out 1 possible solution that is written as an answer below, I will accept it in a few days, but if someone comes up with a better solution, I will negate any answer I post.
Overall I am working on an Apartment Move-Out/Move-In Inspection Application in Django, and in both portions I have universal Locations that must be inspected for each report. I have allowed the InspectionLocations objects to be updated/submitted by clients, which is presenting an issue in how submitted reports should be stored in my Database.
What I want is to use the InspectionLocations table as a blueprint to build an Inspection Report for Move-Ins where the form-fields are generated based on the InspectionLocations objects' location, status, and information attributes/fields.
My issue is right at this point, how do I reference those values as a blueprint to build a report submission when the number of fields in the InspectionLocations can change?
from django.db import models
from apps.units.models import Unit
class Inspections(models.Model):
class Meta:
abstract = True
id = models.AutoField(primary_key=True)
inspection_date = models.DateField()
submitted_by = models.ForeignKey(
'users.CustomUser',
default=None,
null=True,
on_delete=models.SET_NULL,
db_column='submitted_by')
last_update = models.DateTimeField(auto_now=True)
date_added = models.DateTimeField(auto_now_add=True, editable=False)
class MoveInInspections(Inspections):
unit = models.ForeignKey(Unit, on_delete=models.CASCADE, db_column='unit_id')
# should have reference to all InspectionLocation items as reference for submission, how?
class MoveOutInspections(Inspections):
unit = models.ForeignKey(Unit, on_delete=models.CASCADE, db_column='unit_id')
date_notice_given = models.DateField(blank=True, null=True, default=None)
date_vacated = models.DateField(blank=True, null=True, default=None)
# should have reference to all InspectionLocation items as reference for submission, how?
class InspectionLocations(models.Model):
'''
Defualt Inspection Locations are created when a
client is created using code like this:
InspectionLocation.objects.get_or_create(location='Living Room')
InspectionLocation.objects.get_or_create(location='Dining Room')
InspectionLocation.objects.get_or_create(location='Kitchen')
InspectionLocation.objects.get_or_create(location='Bedroom')
InspectionLocation.objects.get_or_create(location='Bathroom')
InspectionLocation.objects.get_or_create(location='Other')
'''
id = models.AutoField(primary_key=True)
location = models.CharField(max_length=50)
status = models.BooleanField(default=None)
information = models.TextField(default=None, blank=True)
I have tried ManyToMany fields and FKs but I cannot seem to get the logic working as anytime an object references an InspectionLocations object it is universally changing data for every report, which is leading be to the idea that I somehow need to use it as a blueprint.
I didn't post this in my question because it was getting long, but my best option so far seems to be to use a Django JSONField (as I am using Postgres), like so:
from django.contrib.postgres.fields import JSONField
class MoveInInspections(Inspections):
unit = models.ForeignKey(Unit, on_delete=models.CASCADE, db_column='unit_id')
data = JSONField()
class MoveOutInspections(Inspections):
unit = models.ForeignKey(Unit, on_delete=models.CASCADE, db_column='unit_id')
date_notice_given = models.DateField(blank=True, null=True, default=None)
date_vacated = models.DateField(blank=True, null=True, default=None)
data = JSONField()
To where I store the values of the InspectionLocations object's in a Dictionary

How to consider a related field in unique key in Django?

I have a following django models:
class Entity(models.Model):
name = models.CharField(max_length=255)
class EntityProp(models.Model):
entity = models.ForeignKey('Entity')
key = models.CharField(max_length=255)
value = models.CharField(max_length=255, blank=True, default='')
I want to make a unique key for Entity that includes name and related EntityProp. For example, I want two entities with the same name and different set of related EntityProp instances to be different. But I want to keep the same Entity if they have identical names and EntityProp instances. Is there an elegant way to do it in Django?
As a reserve way I can create an additional field in Entity called props_hash that contains a hash of the identifiers of all related instances, but it wouldn't be easy to support such structure, I think. So I believe there's a better way.
Here you go
class Entity(models.Model):
name = models.CharField(max_length=255)
class EntityProp(models.Model):
entity = models.ForeignKey('Entity')
key = models.CharField(max_length=255)
value = models.CharField(max_length=255, blank=True, default='')
class Meta:
unique_together = ('key', 'entity')
Source: https://docs.djangoproject.com/en/dev/ref/models/options/#unique-together

Django Model Design using GenericForeignKey

I have a Scheme model that can have 2 rewards assign to it only, one for a member of the scheme and the other for their friend.
Below is how I have design the model for this, but now I'm starting to question the design, Is the link to Scheme and rewards incorrect? Should I have the relationship the other way around on abstract reward instead?
Scheme:
class Scheme(models.Model):
name = models.CharField(max_length=60)
participant_reward_content_type = models.ForeignKey(ContentType,
editable=False,
related_name='%(app_label)s_%(class)s_as_participant',
null=True, blank=True
)
participant_reward_object_id = models.PositiveIntegerField(null=True, blank=True)
participant_reward = generic.GenericForeignKey('participant_reward_content_type', 'participant_reward_object_id')
friend_reward_content_type = models.ForeignKey(ContentType,
editable=False,
related_name='%(app_label)s_%(class)s_as_friends',
null=True, blank=True
)
friend_reward_object_id = models.PositiveIntegerField(null=True, blank=True)
friend_reward = generic.GenericForeignKey('friend_reward_content_type', 'friend_reward_object_id')
Rewards:
class AbstractReward(models.Model):
"""
Abstract reward common information shared for all rewards.
"""
description = models.CharField(max_length="150")
active = models.BooleanField(default=True)
#scheme = models.ForeignKey(Scheme, null=True,)
class Meta:
abstract = True
class SingleVoucherReward(AbstractReward):
"""
Single-use coupons are coupon codes that can only be used once
"""
pass
class Meta:
app_label = 'schemes'
class MultiVoucherReward(AbstractReward):
"""
A multi-use coupon code is a coupon code that can be used unlimited times.
"""
code = models.CharField(max_length=200)
expiry = models.DateTimeField(null=True)
class Meta:
app_label = 'schemes'
class CustomReward(AbstractReward):
"""
A reward class used when it can't be handled or they would like to
handle reward fulfillment themselves.
"""
pass
class Meta:
app_label = 'schemes'
I would recommend keeping it really simple - http://en.wikipedia.org/wiki/KISS_principle
Given the similarity in data definitions of the 3 types of Reward I'd lose the inheritance altogether and just give it a type selection:
class Reward(models.Model):
SINGLE = 'Single'
MULTI = 'Multi'
CUSTOM = 'Custom'
TYPE_CHOICES = (
(SINGLE, 'Single'),
(MULTI, 'Multi'),
(CUSTOM, 'Custom'),
)
description = models.CharField(max_length="150")
active = models.BooleanField(default=True)
type = models.CharField(max_length=10, choices=TYPE_CHOICES, default=SINGLE)
code = models.CharField(max_length=200, blank=True)
expiry = models.DateTimeField(null=True)
Two Scoops of Django - which is a great reference for how to approach things in Django - also recommends this approach.
This also means that you don't need the GenericForeignKey and can have simple foreign keys, massively reducing the complexity again:
class Scheme(models.Model):
name = models.CharField(max_length=60)
participant_reward = models.ForeignKey('Reward', null=True, blank=True)
friend_reward = models.ForeignKey('Rewards', null=True, blank=True)
Built in stuff like the Django admin and ModelForms will just work out of the box with this approach.
Some may not like the verbosity of the TYPE_CHOICES but it is so simple and clear to maintain.
I also realize that you may end up with methods on the Reward class that have to modify behaviour for different types e.g.:
if self.type = CUSTOM:
pass
but again this is very simple to maintain. You could use Proxy Models if the code starts to really diverge.
Some may argue that this is not 'Pythonic' but we are not handling pure python classes here, and besides the Zen of Python states as its third principle:
Simple is better than complex.
You can make your AbstractReward not so abstract (and rename it to BaseReward), then ForeignKey to it and get actual reward type and object in some method. You will need to make an additional request, but I think it will be the same with GenericForeignKey.

How to access method of "sub-model" in Django?

In Django, I have the following models.py
class Product(RandomPrimaryIdModel):
feature1 = models.CharField(max_length=20, blank=True, null=True)
feature2 = models.CharField(max_length=20, blank=True, null=True)
feature3 = models.CharField(max_length=20, blank=True, null=True)
class Mattress(Product):
category_type = models.CharField(max_length=50)
size = models.CharField(max_length=5)
def category(self):
return "bedding"
category = property(category)
I have the following views.py file
def update(request, id):
product = Product.objects.get(id=id)
...
In this method, update, can I call a method defined in the "Mattress" model from the Product model. For example, I want to write: if product.type == "mattress" where type has been defined in the Mattress Model and Mattress is a sub-model of Product.
Your example seems to sit between two different ways you can go, but is currently not correct. What is happening is that you are creating two tables: Product, and Mattress, and they are completely unrelated. Regardless of the fact that Mattress subclasses Product, it is just inheriting its structure. You cannot query anything in the Product table about a mattress because a mattress is in the Mattress table.
One way to go is to consider a Product just abstract, to be subclassed by actual products:
class Product(RandomPrimaryIdModel):
class Meta:
abstract=True
This will prevent a Product table from being created. Then you would directly query a mattress via: Mattress.objects.filter()
But this seems a bit limiting in terms of introducing many types of products, and having to manage different tables for them. The other way to go is to use a Product table, but use generic relations to support attaching any type of other table as a content object:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Product(RandomPrimaryIdModel):
feature1 = models.CharField(max_length=20, blank=True, null=True)
feature2 = models.CharField(max_length=20, blank=True, null=True)
feature3 = models.CharField(max_length=20, blank=True, null=True)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
With this, you would be able to set the content_object to be a Mattress instance. You can then use the ContentType to query:
p_type = ContentType.objects.get(name="mattress")
Product.objects.filter(content_type=p_type)
This looks like a case of automatic down casting. I needed a similar approach for a shopping cart that held generic 'ProductBase' instances but I needed to access the children's specific functions which were the actual products of type ProductDownloadable, ProductShipped, etc.
Django does not natively support this, but one could code it through introspection or use django-model-utils and once that is installed you could do:
# return a list 'child' classes of Product - in your case Mattresses
mattress_list = Product.objects.all().select_subclasses()
# return the direct 'child' class of Product - in your case Mattress class
mattress = Product.get_subclass(id=some_id) # returns the 'child' subclass
mattress.foo() # executes method on foo on Mattress class (not on Product class)

Categories

Resources