I am creating a simple application that captures the name of a product along with their respective ingredients. I have 2 models defined in my Django app for that
class Product(models.Model):
name = models.CharField(max_length=150, blank=False)
class Ingredient(models.Model):
# 1 ingredient belongs to many products.
products = models.ManyToManyField(Product)
name = models.CharField(max_length=150, blank=False)
As I was looking at random products and their ingredients, I noticed that there are alternative names of to an ingredient. For example, certain manufacturers would put water as, aqua, h2o, eau, purified water, distilled water and so on.
How can I best write a model that is able to capture these alternative names in an efficient manner?
I thought of putting in additional fields such as
alternativeName1 = models.CharField(max_length=150, blank=True)
alternativeName2 = models.CharField(max_length=150, blank=True)
alternativeName3 = models.CharField(max_length=150, blank=True)
But it doesn't seem like a good application design.
I suggest you to create a Variation model having foreign of Ingredient model, name and manufacturer information. This means you have many variations of a ingredient
class Variation(models.Model):
ingredient = models.ForeignKey(Ingredient, related_name="variations")
name = models.CharField(max_length=150, blank=False)
manufacturer = models.CharField(max_length=150, blank=False)
So in future if there are more names for any ingredient you can easily keep it.
Or alternatively you can add a JSONField() to Ingredient model
class Ingredient(models.Model):
...
# add json field and keep all name and manufacturer detail in dictionary
# [{'name':'aqua', 'manufacturer':'vendor name'},{},...]
data = JSONField()
Related
I want to make a flexible online shop which will allow it's admins to create products and add custom product fields without need to program. I did it, but the final database structure is so complicated that I can't figure out how to filter it.
Let's say there are categories with some products attached to it. Each category has only one unique template, the template holds custom fields names and types(int, char). When a product is created, the corresponding template-like fields are written to another model that holds custom fields names and values.
So, how to filter the product model considering its custom fields values? To clarify, let's say someone created smartphones category, created template with fields "Brand" and "Screen size", added some smartphones and wants to filter phones with brand="Apple" and screen size > 4.5 inches.
I hope that makes sense ^_^
Database structure:
class Category(models.Model):
name = models.CharField(max_length=63)
class Product(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE)
name = models.CharField(max_length=63)
price = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(1073741823)], null=True, blank=True)
#Template
class CategoryTemplate(models.Model):
category = models.ForeignKey(Category, on_delete=models.CASCADE, null=True)
name = models.CharField(max_length=255, null=True, blank=True)
#Model that holds template custom fields
class TemplateField(models.Model):
template = models.ForeignKey(CategoryTemplate, on_delete=models.CASCADE)
name = models.CharField(max_length=255, null=True, blank=True)
is_integer = models.BooleanField(blank=True, default=False)
#Values of custom char product fields
class ProductPropertiesChar(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
property_name = models.CharField(max_length=255, null=True, blank=True)
property_value = models.CharField(max_length=255, null=True, blank=True)
#Values of custom integer product fields
class ProductPropertiesInteger(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
property_name = models.CharField(max_length=255, null=True, blank=True)
property_value = models.IntegerField(validators=[MinValueValidator(0), MaxValueValidator(1073741823)], null=True, blank=True)
Maybe this will work. Firstly, I'd strongly recommed using explicit related names!
class ProductPropertiesChar(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE,
related_name='charprop')
...
Simple case: all Products related to a single specified ProductPropertiesChar (the default related name is too horrible to type)
results = Product.objects.filter( charprop__property_name='Brand',
charprop__property_value='Apple' )
You can combine several values with __in or use the other usual __ lookups. You should also be able to .exclude(...).
results = Product.objects.filter( charprop__property_name='Brand',
charprop__property_value__in = ['Apple','Samsung','Google'] )
You ought to be able to use Q objects
q1 = Q( charprop__property_name='Brand',charprop__property_value='Apple' )
q2 = Q( intprop__property_name='ScreenSize', intprop__property_value__gte=130 )
I'm pretty sure or will work
results = Product.objects.filter( q1 | q2 )
I'm not quite so sure about and because you are following the related name to two different objects
results = Product.objects.filter( q1 & q2 ) # not sure
You may instead need to use .intersection (doc here)
qs1 = Product.objects.filter( q1)
qs2 = Productr.objects.filter( q2)
results = qs1.intersection( qs2)
See also .union, .difference
At this poimt I'll admit I'm talking about things I have read about but never tried. You will have to experiment, and read the Django docs over and over again!
I'm trying to figure out if what's the best way to implement this behavior:
I have an object of type "Recipe", whose related model "IngredientRecipe" is obtained from a list of "Products" with information about its "Supplier". A recipe may have "beef" as an ingredient and this ingredient is supplied by several suppliers. What I want to obtain is a recipe whose list of ingredients corresponds to the ingredients supplied by the suppliers of the selected location.
For example:
https://api.url/recipes/34/?location=1
This would return the detail of the recipe with id=34 and the list of ingredients but from the location with id=1. That is, the price of the ingredient "beef" will be the one corresponding to the supplier of the location id=1.
models.py:
class Recipe(models.Model):
user = models.ForeignKey(User, null=True, on_delete=models.CASCADE, related_name='user_recipes')
title = models.CharField(_('Recipe title'), max_length=255, blank=True)
class IngredientRecipe(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='products')
recipe = models.ForeignKey(Recipe, on_delete=models.CASCADE, related_name='ingredients')
quantity = models.FloatField(_('Quantity'))
class Product(models.Model):
name = models.CharField(_('Name'), max_length=255)
price = models.FloatField(_('Sale price'), default=0)
supplier = models.ForeignKey(Supplier, blank=True, null=True, related_name='supplier_products',
on_delete=models.CASCADE)
class Supplier(models.Model):
name = models.CharField(_('Name'), max_length=255)
location = models.ForeignKey(Location, on_delete=models.CASCADE)
Right now I'm using ModelViewSets and ModelSerializers to render saving my objects.
Thank you very much in advance.
You can related_name='ingredients' on recipe field of IngredientRecipe:
def your_view_name(request, recipie_id):
data = request.query_params # to take the location id query parameter
data._mutable = True # dictionary from frontend immutable sometimes
location_id = data.location
recipie_list = Recipe.objects.filter(ingredients__product__supplier__location__id=location_id) # will get the recipie queryset based on the location.
I have one model:
class Product(models.Model):
slug = models.SlugField(unique=True)
image = models.ImageField(upload_to='media')
title = models.CharField(max_length=150)
description = models.TextField()
short_description = models.CharField(max_length=300)
price = models.DecimalField(max_digits=5, decimal_places=2)
discount_price = models.DecimalField(max_digits=5, decimal_places=2, blank=True, null=True)
stock_quantity = models.PositiveIntegerField(default=10)
in_stock = models.BooleanField(default=True)
on_sale = models.BooleanField(default=False)
date_added = models.DateTimeField(auto_now_add=True, blank=True)
main_category = models.CharField(default='FW', choices=CATEGORY_CHOICES, max_length=2)
additional_category = models.CharField(choices=CATEGORY_CHOICES, max_length=2, blank=True, null=True)
brand = models.CharField(choices=CATEGORY_CHOICES, max_length=2, blank=True, null=True)
size = models.CharField(max_length=2, choices=CATEGORY_CHOICES, default='M', blank=True, null=True)
def __str__(self):
return self.title
Within this model will be various types of products for example, clothes and books.
These will be defined by the main_category and additional_category fields within the model.
If the product being entered into the database is a piece of clothing I will enter values for size and brand. For books, these will obviously be blank.
However, if the product is a piece of clothing, I have clothing for women, clothing for men and clothing for children. I will be able to define these by selecting 'for women' in the main_category field.
In my templates, I would like to dynamically display a page that shows all clothes, one that displays 'clothes for women', another page that shows 'clothes for men' etc etc. And then a page that displays all books, books for teens, books for adults etc, based on the main_category and additional_category.
If i enter a product, and the main category is 'for women' and the additional category is 'plus size', I would like a template to dynamically display all products that have either 'for women' selected in main_category OR additional_category. And a template that displays all plus size clothing, where main_category OR additional_category is 'plus size'.
My trouble comes when I am writing the views. Do I need to provide a view for women only clothes, plus size only clothes, children's clothes, books, books for adults, books for teens etc. Or can I write just ONE view, that outputs the correct data to multiple templates. Basically, do I need one view per template?
I have read this:
https://docs.djangoproject.com/en/3.0/topics/class-based-views/generic-display/
and have taken a look at get_context_data. Can I define multiple get_context_data functions to return different filtered information for different templates, and if so how do I differentiate between them?
I am current learning, and building a dummy online store with Django in order to learn as much as possible, but finding this particularly complex?
Thanks
A cleaner way to do that is to create an abstract model called Product and use it as a base class for different product types, like clothes and books.
Being Product an abstract class means that Django's ORM won't create a Product table on database. Instead, it will create tables for each subclass including the common fields you define on Product class. Check the code below for better understanding:
# models.py
from django.db import models
class Product(models.Model):
image = models.ImageField(upload_to='media')
title = models.CharField(max_length=150)
# ... (other common fields)
class Meta:
abstract = True
class Clothes(Product):
size = models.CharField(max_length=2, choices=CATEGORY_CHOICES, default='M', blank=True, null=True)
# ... (other specific fields)
From django docs.
I have two models that I want to relate with a many to many relationship. BuildingGroup and Building
My idea is that for example Building Group1 contains some Buildings and BuildingGroup2 contains other Buildings.
I think I should have set it up correctly, but the way it works now is that every BuildingGroup contains ALL of my Buildings, always. I can't delete a Building and selectively decide which building belongs to which group.
Here are my models:
class Building(models.Model):
name = models.CharField(max_length=120, null=True, blank=True)
def __str__(self):
return self.name
class BuildingGroup(models.Model):
description = models.CharField(max_length=500, null=True, blank=True)
buildings = models.ManyToManyField(Building, blank=True)
Is that the right way to set it up? And if so, how can I change it, so I can group it correctly??
Any help is highly appreciated!
You have to change your database relations like below
class Building(models.Model):
name = models.CharField(max_length=120, null=True, blank=True)
group = models.ManyToMany(BuildingGroup, related_name="buildings")
def __str__(self):
return self.name
class BuildingGroup(models.Model):
name = models.CharField(max_length=500, null=True, blank=True)
Now, query like
group = BuildingGroup.objects.get(pk=10) # just a random id
rel_buildings = group.buildings.all()
I have an Event model. Events can have many 'presenters'. But each presenter can either 1 of 2 different types of profiles. Profile1 and Profile2. How do I allow both profiles to go into presenters?
This will be 100% backend produced. As to say, admin will be selecting "presenters".
(Don't know if that matters or not).
class Profile1(models.Model):
user = models.ForeignKey(User, null=True, unique=True)
first_name = models.CharField(max_length=20, null=True, blank=True)
last_name = models.CharField(max_length=20, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
about = models.TextField(null=True, blank=True)
tags = models.ManyToManyField(Tag, null=True, blank=True)
country = CountryField()
avatar = models.ImageField(upload_to='avatars/users/', null=True, blank=True)
score = models.FloatField(default=0.0, null=False, blank=True)
organization = models.CharField(max_length=2, choices=organizations)
class Profile2(models.Model):
user = models.ForeignKey(User, null=True, unique=True)
first_name = models.CharField(max_length=20, null=True, blank=True)
last_name = models.CharField(max_length=20, null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
about = models.TextField(null=True, blank=True)
tags = models.ManyToManyField(Tag, null=True, blank=True)
country = CountryField()
avatar = models.ImageField(upload_to='avatars/users/', null=True, blank=True)
score = models.FloatField(default=0.0, null=False, blank=True)
...
class Event(models.Model):
title = models.CharField(max_length=200)
sub_heading = models.CharField(max_length=200)
presenters = ManyToManyField(Profile1, Profile2, blank=True, null=True) ?
...
# I've also tried:
profile1_presenters = models.ManyToManyField(Profile1, null=True, blank=True)
profile2_presenters = models.ManyToManyField(Profile2, null=True, blank=True)
# is there a better way to accomplish this?...
I think you have a desing problem here. In my opinion, you must think what is a Presenter and what's the different between a Presenter with "profile 1" and with "profile 2". What are you going to do with this models? Are you sure there are just two profiles? Is there any chance that, in some time from now, a different profile ("profile 3") appears? And profile 4? and profile N?
I recommend you to think again about your models and their relations. Do NOT make this decision thinking of how difficul/easy will be to handle these models from django admin. That's another problem and i'll bet that if you think your models a little bit, this won't be an issue later.
Nevertheless, i can give you some advice of how to acomplish what you want (or i hope so). Once you have think abount how to model these relations, start thinking on how are you going to write your models in django. Here are some questions you will have to answer to yourself:
Do you need one different table (if you are going to use SQL) per profile?
If you cannot answer that, try to answer these:
1) What's the difference between two different profiles?
2) Are there more than one profile?
3) Each presenter have just one profile? What are the chances that this property changes in near future?
I don't know a lot about what you need but i think the best option is to have a model "Profile" apart of your "Presenter" model. May be something like:
class Profile(models.Model):
first_profile_field = ...
second_profile_field = ...
# Each presenter have one profile. One profile can "represent"
# to none or more presenters
class Presenter(models.Model):
first_presenter_field = ....
second_presenter_field = ....
profile = models.ForeignKey(Profile)
class Event(models.Model):
presenters = models.ManyToManyField(Presenter)
....
This is just an idea of how i imagine you could design your model. Here are some links that may help you once you have design your models correctly and have answered the questions i made to you:
https://docs.djangoproject.com/en/dev/topics/db/models/#model-inheritance
https://docs.djangoproject.com/en/dev/misc/design-philosophies/#models
http://www.martinfowler.com/eaaCatalog/activeRecord.html
And to work with the admin once you decide how your design will be:
https://docs.djangoproject.com/en/dev/ref/contrib/admin/
EDIT:
If i'm not wrong, the only difference between profile 1 and 2 fields is the "organization" field. Am i right? So i recommend you to merge both models since they are almost the same. If they have different methods, or you want to add different managers or whatever, you can use the proxy option of django models. For example, you can do this:
class Profile(models.Model):
#All the fields you listed above, including the "organization" field
class GoldenProfile(models.Model):
#you can define its own managers
objects = GoldenProfileManager()
....
class Meta:
proxy = True
class SilverProfile(models.Model):
....
class Meta:
proxy = True
This way, you can define different methods or the same method with a different behaviour in each model. You can give them their own managers, etcetera.
And the event class should stay like this:
class Event(models.Model):
title = models.CharField(max_length=200)
sub_heading = models.CharField(max_length=200)
presenters = ManyToManyField(Profile, blank=True, null=True)
Hope it helps!