django inheritance hurts my brain - django

I'm making a beer website, and I'm stuck. I need to have a recipe that i can relate to many different ingredients. Is there a way to create a model "Ingredient", and inherit many different models: "Hop", "Grain", etc. I would want to do this so I have my relationship between recipe and ingredient, and i don't have to create 100 different relationships to cover each kind of ingredient.
Is this possible?

Although I would personally advise against it in most cases since table inheritance is a pain (imho) in Django.
You can do it like this:
class Recipe(models.Model):
name = models.CharField()
class Ingredient(models.Model):
name = models.CharField()
recipes = models.ManyToManyField(Recipe, related_name='%(app_label)s_%(class)s')
class Hop(Ingredient):
pass
class Grain(Ingredient):
pass

Related

How to make one to many relationship that uses many diffrent models in Django?

I'm writing one of first Django apps and
I have recipe model like this:
class Recipe(models.Model):
name = models.CharField(max_length=64)
and also many other models for steps in recipe:
class BaseStep(models.Model):
recipe = models.ForeignKey('recipe.Recipe', on_delete=models.CASCADE)
class TextStep(BaseStep):
text = models.CharField(max_lengh=4096)
class ListStep(BaseStep):
list_elements = models.CharField(max_length=2048)
class TimerStep(BaseStep):
time = models.PositiveIntegerField()
above models are simplified, what makes them pointless but I need them this way. I know that normally I would specify ForeignKey in BaseStep so that I have some reference, like this:
recipe = models.ForeignKey('recipe.Recipe', related_name='steps', on_delete=models.CASCADE)
but this simply doesn't work, because it's inherited by child models, with is not OK because related_name needs to be unique. In the end I need to have field steps in Recipe that will return array or queryset of all the steps. I know that Proxy Models can do something like that but using it here is stupid. Now that I'm thinking, is making a #property function in Recipe, that will query all steps and return python array out of them class a good idea? I need to serialize it in the end, for rest endpoint.
Take a look at https://django-polymorphic.readthedocs.io/en/stable. This offers tools to easily work with model inheritage.

Django: child models with the same properties, but different attributes on those properties

I have two models in my database that, if I don't do anything "creative" will look something like this:
class AbstractModel(models.Model):
uuid = models.UUIDField(default=uuid.uuid4)
class Meta:
abstract = True
class ModelA(AbstractModel):
name = models.CharField(max_length=128)
class ModelB(AbstractModel):
name = models.CharField(max_length=128, blank=True, null=True)
The only real difference between ModelA and ModelB is that I want ModelA's name property to be non-nullable, but writing it like this, especially when you consider that I've got about 10 different fields to write in much the same situation, feels very not-DRY. Surely, there's a better/smarter way?
Note that this isn't about validation, so moving the blank-checking up into a form isn't going to solve the problem. I want the database column to be NOT NULL for ModelA and NULL for ModelB.
ModelA and ModelB probably represent entities which don't have as much in common as it appears when you list their properties. The fact that one needs values just to live and the other doesn't suggests their behavior and responsibilities require independant representation. They do different things for you.

Django - Model with more than two foreign keys

I want to give a classification to every recipe and I need 3 levels (subject -> category -> subcategory). I have the following models:
class Recipe(models.Model):
content = HTMLField()
...
class Subject(models.Model):
name = models.CharField()
class Category(models.Model):
subject = models.ForeignKey(Subject)
name = models.CharField()
class Subcategory(models.Model):
category = models.ForeignKey(Category)
name = models.CharField()
Initially I was thinking to add a ForeignKey to the Recipe model, something like:
subcategory = models.ForeignKey(Subcategory)
So I could be able to go from the subcategory to the subject, doing a simple join.
But maybe is better to create a new table to improve performance and to have a scalable system, this is my thought:
class Classification(models.Model):
recipe = models.ForeignKey(Recipe)
subject = models.ForeignKey(Subject)
category = models.ForeignKey(Category)
subcategory = models.ForeignKey(Subcategory)
Using this last solution, I want to select the subcategory from the Recipe admin panel and then automatically fill the other fields (category and subject). My question is: how can I do that?
Thank you!
Your first design is actually the best. It is a completely denormalized database design. The second design has the possibility of putting your recipe in a subcategory unrelated to your category which is unrelated to your subject. So, what you gain in performance might be lost in data integrity.
The classes Subject, Category, Subcategory and Recipe (with ForeignKey) creates fully normalized database design adding Classification is a part of denormalizing database (for example for performance issues).
Answering your question:
recipe = Recipe.objects.get(pk=1) # just assume that you have object instance under recipe
r_subcategory = recipe.subcategory
r_category = r_subcategory.category
r_subject = r_category.subject
new_classification = Classification(subject=r_subject, category=r_category, subcategory=r_subcategory, recipe=recipe)
new_classification.save()
I would really like to know if there is a way to create it in one database request (I could easily produce sql request for that, but I do not know how to achieve the same with Django ORM)

assign a property to a many to many relationship

I'm making a couple of models for cooking, in django, Recipes and Ingredients.
I use a many to many field to relate both. Now, I'd like to assign a number to each relationship, so going from
recipe.ingredients = [sugar,egg]
to
recipe.ingredients = {sugar:200,egg:2}
How can I do that? It is 100% necessary to explicitly build a third model ingredients_recipes? The table should exist already, but I want to know if is possible to use the many to many field directly.
Yes, you need to create the intermediate model with your additional fields. You can then specify the intermediate in a through argument to the ManyToManyField, for example:
class Recipe(models.Model):
#...
ingredients = models.ManyToManyField(Ingredients, through="RecipeIngredients")
class RecipeIngredients(models.Model):
recipe = models.ForeignKey(Recipe)
ingredient = models.ForeignKey(Ingredient)
amount = models.IntegerField()
class Meta:
unique_together = ('recipe', 'ingredient')
See also the official documentation: Extra fields on many-to-many relationships

How to fetch the top two products for each product type?

Let's say I have two models looking like this:
class ProductType(models.Model):
product_type = models.CharField(max_length=100)
class Product(models.Model):
name = models.CharField(max_length=200)
slug = models.SlugField()
product_type = models.ForeignKey(ProductType)
score = models.PositiveIntegerField(default=0)
Now I want to fetch the top two products (the two with highest score) from each ProductType.
So if I have Phones, Computers, TVs as ProductTypes I want the two best Phones, Computers, TVs.
As I don't even know how to do this in MySQL I've tried searching for the MySQL-solution but the ones I find is extremely complex and this doesn't feel like an extremely complex thing to do.
I am leaning towards making an own model for the top products and have a cronjob to fix this, but I'd like to see if there's an easier solution to this.
Well, you could make a method inside of the ProductType class that returns the top two results for its products:
class ProductType(models.Model):
product_type = models.CharField(max_length=100)
def TopTwoProducts(self):
return self.product_set.order_by('-score')[0:2]
Then you would just do something like:
for type in ProductType.objects.all():
type.TopTwoProducts()
While adam's solution is correct, a more django-ish way would be to use a manager.
See Managers versus class methods on James Bennett's blog
Among other advantages :
a manager carries all query-specific code, while avoiding to clutter the model class
the same manager class can be shared among several classes
the manager can be used directly on a model class, or via a one-to-many or m2m relation
Thus, for the above question :
from django.db import models
from django.db.models.manager import Manager
class ProductScoreManager(Manager):
use_for_related_field = True
def top_score(self, limit=2):
return self.get_query_set().order_by('-score')[:limit]
Then add this manager class as default manager for Product :
class Product(models.Model):
...
objects = ProductScoreManager()
...
Now, thanks to objects overriding the default manager, and use_for_related_field allowing its use in relation queries, the top_score method can be used in any model related to products.
myproducttype = ProductType.objects.get(...)
myproducttype.products.top_score() # return top 2 products
This allows a more consistent syntax : the related products is always accessed via products, top_score acting as a filter. Additionally, ProductType class is no more cluttered with Product's query logic.
Just filter out product type from Product model slice them like this -
product_type_list = ProductType.objects.value("id")
for product_type in product_type_list:
Product.objects.filter(
product_type=product_type
).order_by("-score")[0:2]