Let's say I have a model of sandwiches, and I want to say what protein you want to put on the sandwich:
class Sandwich(models.Model):
protein_choices = (
('n', 'Please Choose'),
('e1', 'Eggplant'),
('e2', 'Hummus'),
('v1', 'Provolone'),
('v2', 'Egg'),
('p1', 'Fish'),
('c1', 'Beef'),
('c2', 'Chicken'),
('c3', 'Pork'),
)
protein = models.CharField(
max_length=2,
choices=protein_choices,
default='n',
)
How could I make the choices categorized by vEgan, Vegetarian, Pescatarian, or Carnivore?
I want to be able to check its category (is the sandwich Vegan[assuming categorizations don't overlap]?), and I have been using a model.Manager, but I want to ensure that all choices have a single category (I think this is the missing link, and don't think testing is the right way) and only one choice is selected (which is already handled by the status structure presented).
Should this be handled in a model.Form, a 1->M relationship with some other structure, or through model.Managers and something else?
Another option is an in-memory data structure and interface that maps your status keys to their appropriate category. For example you could use an Enum with a category property as the basis for your Choices. If the relationships between those strings ends up being something you want to manage in the database you can always refactor and add a migration.
As you've noted building a tree structure in your models as others have suggested makes for a lot of joins.. and in that case it might be useful to take a look at django-treebeard or django-categories and check if it's reasonable to take on that additional dependency.
I think having separate models for protein and category will be good.
Example:
class Protein(models.Model):
name = models.CharField(max_length=20)
class ProteinCategory(models.Model):
protein = models.ForeignKey(to="Protein")
name = models.CharField(max_length=20)
class Sandwich(models.Model):
protein = models.ForeignKey(to="Protein")
Use ManyToManyField instead of ForeignKey in Sandwich model, if a Sandwich contains many Protein.
Use OneToOneField in ProteinCategory model, if a Protein can be in one and only one ProteinCategory.
I suggest using different models for this task.
Continuing with your example, you could have the proteins, which each belong to a single category (as per your request in the comments to your question):
class ProteinCategory(models.Model):
name = models.CharField(max_length=20)
class Protein(models.Model):
name = models.CharField(max_length=20)
category = models.ForeignKey(to=ProteinCategory)
Then you could assign a single protein (as per your request) for each sandwich:
class Sandwich(models.Model):
name = models.CharField(max_length=20)
protein = models.ForeignKey(to=Protein)
Does this seem to solve your issue?
Related
I am new to using Many-Many fields and I really could not find a good example that explained on how to go on with this task.
I currently have two models modelJob and modelSkillSubscription now modelJob contains a Many-To-Maney fields of skills.
What I would like to do is
retrieve modelSkillSubscription if they contain any of the skills that I have obtained. Say modelJob contains [SkillA,SkillB,SkillC]. I would like to pass this to a filter and obtain all modelSkillSubscription that contains any of the skills from the List avoiding duplicates.
class modelJob(models.Model):
skills = models.ManyToManyField(modelSkill,blank=True)
class modelSkillSubscription(models.Model):
employer = models.ForeignKey(modelEmployer, on_delete=models.CASCADE, default=None, blank=True)
skills = models.ManyToManyField(modelSkill, blank=True)
Any suggestions on how I can accomplish this ?
You can filter the SkillSubscription models based on an existing Job instance (I'd drop the leading model, making the model name shorter and compliant with Python “CamelCase” class naming conventions):
job = Job.objects.get(id=1)
subscriptions = (SkillSubscription.objects
.filter(skills__in=job.skills.all())
.distinct()
)
This selects all subscriptions that have at least one skill in common with the job.
You can also wrap the job selection in the same query if you know the filter criteria:
subscriptions = (SkillSubscription.objects
.filter(skills__in=Job.objects.filter(
# your filter criteria
)
.values('skills'))
.distinct()
)
Is there a by the book way of allowing a user to add columns to a sites database table. For example, if the site was about animals, one user might want to have stats like, 'walks per week' and 'type of food' about their breed of dog. but another user might want to keep track of how much milk their goat is producing.
So if i have an 'Animal' class with come basic info. like, 'breed', 'animal name', 'DOB', 'DOD'. But then, in the front end have a form that will allow the users to add all the other columns they would like.
Is this possible? hope I've explained it well enough.
#WillemVanOnsem already mentioned some good options in the comments. I'm going to chime in to say that modifying your schema's structure based on user input is an extremely bad idea and opens another avenue for abuse... for Django in particular, it means you either can't use the ORM's migration facilities for some of your models, or you probably have to do some really awful automation.
If your animal types are well-defined and consistent, you can consider (carefully) making them subclasses of the Animal model. Otherwise, this would be the simplest way to handle it (note that the following isn't valid code, it needs required arguments for the field types):
class AnimalAttribute(models.Model):
animal = models.ForeignKey(Animal)
name = models.CharField()
value = models.CharField()
This works best if attributes aren't shared, e.g. users are directly inputting their animals' names and attributes, not picking from an existing list.
If you need to provide a normalized list of attributes users can pick from (actual EAV, which is something you should avoid if possible, since it moves some of your data structure from code into the data persistence layer), doing that in your models is a little more complex. For example:
class Species(models.Model):
name = models.CharField()
class SpeciesAttribute(models.Model):
species = models.ForeignKey(Species)
name = models.CharField()
class Animal(models.Model):
name = models.CharField()
species = models.ForeignKey(Species)
class AnimalAttributeValue(models.Model):
animal = models.ForeignKey(Animal)
attribute = models.ForeignKey(SpeciesAttribute)
value = models.CharField()
so I'm trying to create a good way of modelling both "houses" and "house groups".
Houses and house groups are extremely similar in that they both carry a description and have related pricing information.
However, "bookings" can only be assigned to Houses and not to HouseGroups.
At the moment, my model looks like this:
class Houselike(models.Model):
max_guests = models.IntegerField()
name = models.CharField(max_length=20)
description = models.TextField(blank=True)
class House(Houselike):
pass
class HouseGroup(Houselike):
houses = models.ManyToManyField(House)
Semantically, this actually very close to what I want. However, in the database, this leads to there being two tables that both only have a single field "houselike_ptr_id" referring to the "Houselike" base object.
Checking whether a Houselike object is a House or a Housegroup thus involves looking in two different tables.
A more efficient alternative would be to do:
class Houselike(models.Model):
max_guests = models.IntegerField()
name = models.CharField(max_length=20)
description = models.TextField(blank=True)
is_group = models.BooleanField()
houses = models.ManyToManyField(House)
This results in only 1 extra field in the "houselike" table, and the other table containing the related houses is only hit if we actually look them up. This is the best solution from a storage point of view IMHO.
However, this isn't quite as good from a semantic point of view: Houses and Housegroups are similar, but different objects.
Also, this allows for stuff like housegroups containing other housegroups, non-groups containing houses, things I have to all check manually.
I also really like being able to explicitly work with House and HouseGroup objects. Representing them both with the same class just feels wrong.
Is there a better way to do this?
EDIT:
I forgot to mention that pricing information (as well as other entities) can be associated with either a House or a Housegroup, and is implemented (roughly) as follows:
class PricePeriod(models.Model):
house = models.ForeignKey(Houselike, on_delete=models.CASCADE)
arrival_date = models.DateField()
# Date of last departure date
departure_date = models.DateField()
price = models.DecimalField(max_digits = 10, decimal_places=2)
This is why I don't simply make the Houselike an abstract model, because these other objects are related to it.
Turns out, this is something called "single table inheritance", which is perfect in my case.
And, this being the Internet, there's an app for that: https://github.com/craigds/django-typed-models
from typedmodels.models import TypedModel
# Create your models here.
class Houselike(TypedModel):
max_guests = models.IntegerField()
name = models.CharField(max_length=20)
description = models.TextField(blank=True)
class House(Houselike):
pass
class HouseGroup(Houselike):
houses = models.ManyToManyField(House)
This resulted in pretty much exactly what I was asking: a single table in the database, and an explicit, semantically-correct model in Python/Django.
Now just I just need to fix my awful naming...
I have the following models:
class Deal(models.Model):
date = models.DateTimeField(auto_now_add=True)
retailer = models.ForeignKey(Retailer, related_name='deals')
description = models.CharField(max_length=255)
...etc
class CustomerProfile(models.Model):
saved_deals = models.ManyToManyField(Deal, related_name='saved_by_customers', null=True, blank=True)
dismissed_deals = models.ManyToManyField(Deal, related_name='dismissed_by_customers', null=True, blank=True)
What I want to do is retrieve deals for a customer, but I don't want to include deals that they have dismissed.
I'm having trouble wrapping my head around the many-to-many relationship and am having no luck figuring out how to do this query. I'm assuming I should use an exclude on Deal.objects() but all the examples I see for exclude are excluding one item, not what amounts to multiple items.
When I naively tried just:
deals = Deal.objects.exclude(customer.saved_deals).all()
I get the error: "'ManyRelatedManager' object is not iterable"
If I say:
deals = Deal.objects.exclude(customer.saved_deals.all()).all()
I get "Too many values to unpack" (though I feel I should note there are only 5 deals and 2 customers in the database right now)
We (our client) presumes that he/she will have thousands of customers and tens of thousands of deals in the future, so I'd like to stay performance oriented as best I can. If this setup is incorrect, I'd love to know a better way.
Also, I am running django 1.5 as this is deployed on App Engine (using CloudSQL)
Where am I going wrong?
Suggest you use customer.saved_deals to get the list of deal ids to exclude (use values_list to quickly convert to a flat list).
This should save you excluding by a field in a joined table.
deals = Deals.exclude( id__in=customer.saved_deals.values_list('id', flat=True) )
You'd want to change this:
deals = Deal.objects.exclude(customer.saved_deals).all()
To something like this:
deals = Deal.objects.exclude(customer__id__in=[1,2,etc..]).all()
Basically, customer is the many-to-many foreign key, so you can't use it directly with an exclude.
Deals saved and deals dismissed are two fields describing almost same thing. There is also a risk too much columns may be used in database if these two field are allowed to store Null values. It's worth to consider remove dismissed_deals at all, and use saved_deal only with True or False statement.
Another thing to think about is move saved_deals out of CustomerProfile class to Deals class. Saved_deals are about Deals so it can prefer to live in Deals class.
class Deal(models.Model):
saved = models.BooleandField()
...
A real deal would have been made by one customer / buyer rather then few. A real customer can have milions of deals, so relating deals to customer would be good way.
class Deal(models.Model):
saved = models.BooleanField()
customer = models.ForeignKey(CustomerProfile)
....
What I want to do is retrieve deals for a customer, but I don't want to include deals that they have dismissed.
deals_for_customer = Deals.objects.all().filter(customer__name = "John")
There is double underscore between customer and name (customer__name), which let to filter model_name (customer is related to CustomerProfile which is model name) and name of field in that model (assuming CutomerProfile class has name attribute)
deals_saved = deals_for_customer.filter(saved = True)
That's it. I hope I could help. Let me know if not.
I am looking to find a way to annotate a queryset with the counts of a subset of related items. Below is a subset of my models:
class Person(models.Model):
Name = models.CharField(max_length = 255)
PracticeAttended = models.ManyToManyField('Practice',
through = 'PracticeRecord')
class Club(models.Model):
Name = models.CharField(max_length = 255)
Slug = models.SlugField()
Members = models.ManyToManyField('Person')
class PracticeRecord(PersonRecord):
Person = models.ForeignKey(Person)
Practice = models.ForeignKey(Practice)
class Practice(models.Model):
Club = models.ForeignKey(Club, default = None, null = True)
Date = models.DateField()
I'm looking to make a queryset which annotates the number of club specific practices attended by a person. I can already find the total number of practices by that person with a query of Person.objects.all().annotate(Count('PracticeRecord'))
However I would like someway to annotate the number of practices that a person attends for a specific club.
I would prefer something using the django ORM without having to resort to writing raw SQL.
Thanks.
However I would like someway to annotate the number of practices that a person attends for a specific club.
Let us see.
First, find the specific club.
club = Club.objects.get(**conditions)
Next, filter all Persons who have practiced at this club.
persons = Person.objects.filter(practicerecord__Practice__Club = club)
Now, annotate with the count.
q = persons.annotate(count = Count('practicerecord'))
Edit
I was able to successfully make this work in my test setup: Django 1.2.3, Python 2.6.4, Postgresql 8.4, Ubuntu Karmic.
PS: It is a Good Idea™ to use lower case names for the fields. This makes it far easier to use the double underscore (__) syntax to chain fields. For e.g. in your case Django automatically creates practicerecord for each Person. When you try to access other fields of PracticeRecord through this field you have to remember to use title case.
If you had used lower case names, you could have written:
persons = Person.objects.filter(practicerecord__practice__club = club)
# ^^ ^^
which looks far more uniform.
PPS: It is Count('practicerecord') (note the lower case).
I'm afraid that raw sql is the only option here. Anyway it's not that scary and hard to manage if you put it to model manager.