I'm trying to represent some movie ratings data in Django. Here is a simplified version of my models that illustrates my problem:
class RatingSystem(models.Model):
"""This denotes a rating authority and territory in which they operate"""
name = models.CharField(max_length=16)
territory = models.CharField(max_length=32)
class Rating(models.Model):
"""This represents a rating designation used by a rating system."""
code = models.CharField(max_length=16)
description = models.TextField()
system = models.ForeignKey(RatingSystem)
class FilmRating(models.Model):
"""This is a rating for a film and the reason why it received the rating.
Each film can have many ratings, but only one per rating system.
"""
rating = models.ForeignKey(Rating)
film = models.ForeignKey('Film')
reason = models.TextField()
class Film(models.Model):
"""Data for a film."""
title = models.CharField(max_length=64)
synopsis = models.TextField()
ratings = models.ManyToManyField(Rating, through=FilmRating)
As the comments indicate, each Film can have multiple ratings, but only one rating per rating system. For instance, a film cannot be rated both 'R' and 'PG' by the MPAA. However, it can be rated 'R' by the MPAA and '15' by the BBFC.
I'm struggling to formalize this constraint in Django. I'd like to do:
unique_together = ('film', 'rating__system')
in FilmRating but following a relationship like that doesn't seem to be allowed. If I were using pure SQL, I would make code and system a composite primary key in Rating, then make a unique constraint on system, and film in FilmRatings. Unfortunately, Django does not support composite keys. I've considered overriding the save() method of FilmRating, but I'd prefer to have the constraint at the database level if possible.
Anyone have any idea how to do this? Restructuring the tables would be fine too if it would help.
EDIT: Updated answer based on #JoshSmeaton's and #MSaavedra's comments
Using Django's syncdb hook, you could run the ALTER TABLE statements directly on the database. Django will raise an IntegrityError if the unique constraint is violated, even though that constraint wasn't defined by django.
Then, adding the constraint to validate_unique would reduce developer confusion later on and safely enforce the constraint in Django.
I think you should take a look at validate_unique
This same problem some years ago in stackoverflow
The Django Docs
You can use model field validation.
https://docs.djangoproject.com/en/dev/ref/models/instances/#validating-objects
Related
I have a ManyToMany relationship that indicates a Doctor can have many specialties, but only one of them is the PRIMARY one.
I've designed a custom M2M class as follows:
class Doctor(models.Model):
account = models.ForeignKey(Account, on_delete=models.CASCADE)
specialty = models.ManyToManyField(Specialty, through='DoctorSpecialty')
.....
class Specialty(models.Model):
title = models.CharField(max_length=45)
.....
class DoctorSpecialty(models.Model):
doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
specialty = models.ForeignKey(Specialty, on_delete=models.CASCADE)
default = models.BooleanField(default=True)
The doctor can have many specialties, but only one of them can be the default one. He or she can have many specialties with the default field set as False, but cannot have more than one with the default field set as True
I wanted to do something like this:
class Meta:
constraints = [
models.UniqueConstraint(fields=['doctor', 'specialty', 'default'], name='unique specialty')
]
But this will mean that the doctor can have only one specialty as a default one, and only one other as a non default one.
How can we achieve this with the minimum of code?
PS: I could leave it without constraints and try to validate adding new entries by checking if another default specialty exists, but this will add a lot of overhead and exception raising.
I think there is no way we can achieve this with built-in functions. So I came up with this solution (since no one else answered):
I created another ForeignKey for the Primary Specialty, and ditched the DoctorSpecialty custom M2M class and left the M2M relationship with Specialty. One doctor can have only one primary specialty, and can also choose additional specialties as secondary. Later on in the views, I can put in place an algorithm to remove the primary specialty from the list of specialties when entering additional ones in case there is an existing primary specialty.
I know this is a very basic question. I am learning django and i see the most important part is ForeignKey field and ManyToManyField. They are used ubiquitously. Without understanding those two, a proper model cannot be designed. If i have to design a model with FK relation, i always have to see the example first and try to come with the solution. I cannot confidently design a model cause i have not understand this well. It would be great if someone make me understand so that the picture comes to my head what is FKField, how FKField and MTMField are generated in table with simple english(Language is one of the barrier for me to understand from the documentation).
Here is the model for foreign key
class Category(models.Model):
name = models.CharField()
class Product(models.Model):
name = models.CharField()
category = models.ForeignKeyField(Category, related_name="product")
In django, you can add one instance of a "variable" as a part of a table: That is a ForeignKey.
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=30)
category = models.ForeignKey(Category)
class Category(models.Model):
name = models.CharField(max_length=30)
Here, you will have a SQL table named "[NAME OF YOUR APP]_product" that will have two columns: "name" and "category_id".
You will have an other table named "[NAME OF YOUR APP]_category" that will contain one column "name".
Django will know that when you load a Product, it will have to get its category_id, and then get that element from the category table.
This is because you use a foreignkey: it is one "variable". And it is "Many to One" because you can have many Products having the same Category.
Then you have "Many to Many". Here you can have more than one "variable"
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=30)
category = models.ManyToManyField(Category)
class Category(models.Model):
name = models.CharField(max_length=30)
Here, the difference is that you will get a table named "[NAME OF YOUR APP]_product" with only one column: "name".
Next to that, you will have a table "[NAME OF YOUR APP]_product_category", that will have the columns "product_id" and "category_id".
And one last table that will be "[NAME OF YOUR APP]_category" that will have one column: "name".
The way it works is that Django will get the Product, and see that it have a ManyToMany field of Category.
It will go to "[NAME OF YOUR APP]_product_category" and get the list of ids for the product_id you need, and get them from "[NAME OF YOUR APP]_category".
This is Many to Many because you can have a lot of Products that have each lots of different Category.
If you still don't understand, I will edit this post to add a SQL example of what the database looks like.
(Sorry, this is not really pleasant to read and a really broad way to explain how Django handle things, but I tried to do short and simple statements.)
I'm trying to wrap my head around how I would structure my database tables in the Django webapp I'm writing. I'm a relative newbie to web development, but this is the very first time I've tried to use a database, so bear with me if it's a stupid question.
The webapp goes through each Oscar the Academy gives out and allows the user to select which of some (varying) number of nominations will win an Oscar. The data from each individual session will be publicly available by going to a url like [url].com/answers/[unique id]. The overall data will also be available on a results page. So I've started writing my models file, and this is what I have so far:
from django.db import models
class Nominee(models.Model):
award = models.CharField(max_length=50)
title = models.CharField(max_length=50)
key = modelsCharField(max_length=50)
subtitle = models.CharField(max_length=50)
numVotes = models.IntegerField()
class Session(models.Model):
id = models.IntegerField() # unique id of visitor
bpictureVote = models.ForeignKey(Nominee, related_name = 'nom')
bactorVote = models.ForeignKey(Nominee, related_name = 'nom')
# ... for each award
I was originally thinking of having
class Award(models.Model):
name = models.CharField(max_length=50)
and at the beginning of Nominee,
award = models.ForeignKey(Award, related_name = 'award')
but I couldn't figure out why that would be better than just having award be a part of the Nominee class.
This is really just a start, because I've gotten a bit stuck. Am I on the right track? Should I be doing this totally differently (as I probably should...)? Any thoughts?
Thanks!
You are on the right track.
You need a separate Award class to avoid having to type in award's name every time you create a Nominee. By having a ForeignKey reference you make sure that you can safely rename your award, add additional information about the award (let's say in the future you decide to give each award a separate page with a description and a list of nominees), you also avoid errors which can happen from having a set of different spellings and typos ("Best Engineer Award" and "Best Engineer award"). It also makes sense - your application operates a set of objects: user sessions, nominees and awards.
Few unrelated notes:
You don't need an explicit Session.id field, django ORM creates it for you.
Property names have to be name_with_underscores, not camelCase.
No spaces around "=" in an arguments list: models.ForeignKey(Nominee, related_name='nom').
4 spaces instead of 2 (unless explicitly otherwise specified).
I am not entirely sure, because you do mention multiple nominees per award (assuming this is something like a poll before the actual nomination) a ManyToMany would be your required relation, in order to use also the additional user data.
But in the case you have implemented this as a specific app for nominations and implemented a custom user model then this would be refactored to something else...
Anyway to your current implementation:
class Nominee(models.Model):
title = models.CharField(max_length=50)
key = modelsCharField(max_length=50)
subtitle = models.CharField(max_length=50)
...
class Award(models.Model):
name = models.CharField(max_length=50)
nominees = models.ManyToManyField(Nominee, through='AwardNominees')
...
class AwardNominees(models.Model):
nominee = models.ForeignKey(Nominee)
award = models.ForeignKey(Award)
user = models.ForeignKey(User)
numVotes = models.IntegerField()
....
So it turned out I was thinking about this entirely wrong. I've now completely changed things, and now it's fully functional (!). But in the spirit of full disclosure, I should say that it definitely may not be the best solution. It sure seems like a good one, though, because it's really simple. Now I have only one model:
class Vote(models.Model):
award = models.CharField(...) # Name of the award
title = models.CharField(...) # Title of the nominee
subtitle = models.CharField(...) # Subtitle of the nominee
uid = models.CharField(...) # A 6 character user ID for future access
When I want to show the results of one user's votes, I can use Django's database tools to filter for a certain uid captured in the URL. When I want to tally the votes, I can use a combination of filters and Django's count() to determine how many votes each nominee had for a certain award. Sounds reasonable enough to me!
I am creating a web application to manage robotics teams for our area. In the application I have a django model that looks like this:
class TeamFormNote(models.Model):
team = models.ForeignKey(Team, blank=True, null=True)
member = models.ForeignKey(TeamMember, blank=True, null=True)
notes = models.TextField()
def __unicode__(self):
if self.team:
return "Team Form Record: " + unicode(self.team)
if self.member:
return "Member Form Record: " + unicode(self.member)
Essentially, I want it to have a relationship with team or a relationship with member, but not both. Is there a way to enforce this?
I can only see two viable solutions. First is actually the same as #mariodev suggested in the comment which is to use Genetic foreign key. That will look something like:
# make sure to change the app name
ALLOWED_RELATIONSHIPS = models.Q(app_label = 'app_name', model = 'team') | models.Q(app_label = 'app_name', model = 'teammember')
class TeamFormNote(models.Model):
content_type = models.ForeignKey(ContentType, limit_choices_to=ALLOWED_RELATIONSHIPS)
relation_id = models.PositiveIntegerField()
relation = generic.GenericForeignKey('content_type', 'relation_id')
What that does is it sets up a generic foreign key which will allow you to link to any other model within your project. Since it can link to any other model, to restrict it to only the models you need, I use the limit_choices_to parameter of the ForeignKey. This will solve your problem since there is only one generic foreign key hence there is no way multiple relationships will be created. The disadvantage is that you cannot easily apply joins to generic foreign keys so you will not be able to do things like:
Team.objects.filter(teamformnote_set__notes__contains='foo')
The second approach is to leave the model as it and manually go into the database backend and add a db constaint. For example in postgres:
ALTER TABLE foo ADD CONSTRAINT bar CHECK ...;
This will work however it will not be transparent to your code.
This sounds like a malformed object model under the hood...
How about an abstract class which defines all common elements and two dreived classes, one for team and one for member?
If you are running into trouble with this because you want to have both referenced in the same table, you can use Generic Relations.
I need to make a smart menu, for which I need a ManyToMany relation.
My model is:
from django.db import models
class Health_plan(models.Model):
a = models.IntegerField ()
b = models.IntegerField ()
class Doctors_list(models.Model):
name = models.CharField(max_length=30)
hp_id = models.ManyToManyField(Health_plan)
def __unicode__(self):
return self.name
How do I make this relation in the database ? I was thinking in puting the health_plans (a,b) as columns, and the doctors as rows, with 0s and 1s to identify their covered health_plans.
Someone told me this was a misuse of a ManyToManyField, I don't know wich step to take.
Help appreciated
The approach of puting the health_plans as columns is not necessarily wrong, but it implies that you have a fixed number of health plans and that you will never add a new one.
The traditional approach for many-to-many relationships in relational databases is to introduce a table in the middle. This table will just contain the association between a doctor and a health plan.
If you have a Doctor table that contains:
id name
1 foo
2 bar
And a HealthPlan table:
id model
1 a
2 b
You then add a table Doctor_HealthPlan that is like:
doctor_id healthplan_id
1 2
2 1
2 2
The ManyToMany field type in django will automatically create this table for you. Your code is correct, but you should probably rename hp_id to something like health_plans, since it is a proxy that allows you to access the list of health plans associated to a doctor.
Django's ORM already takes care of the intermediate table so you don't have to "make this relation(ship) in the database", but given your question you obviously need to learn about proper relational model normalisation - if you don't understand the relational model you won't get nowhere with Django's ORM, nor with any other sql stuff FWIW.
For the record, in the relational model, a many to many relationship is modeled as a relation ("table" in SQL) with foreign keys on both other tables, ie:
health_plan(#health_plan_id, name, ...)
doctor(#doctor_id, firstname, lastname, ...)
doctors_health_plans(#health_plan_id, #doctor_id)
So your django models should be:
class HealthPlan(models.Model):
# no need to define an 'id' field,
# the ORM provides one by default
name = models.CharField(....)
class Doctor(models.Model):
firstname = models.CharField(....)
lastname = models.CharField(....)
health_plans = models.ManyToManyField(HealthPlan, related_name="doctors")
Then you'll be able to get all HealthPlans for a Doctor :
doc = Doctor.objects.get(pk=xxxx)
doc.health_plans.all()
and all Doctors for an HealthPlan:
plan = HealthPlan.objects.get(pk=xxxx)
plan.doctors.all()
The FineManual(tm) is your friend as usual...
You just need to save the two models first then add the healthplan instance to the doctors list. Django will handle the rest for you .
For example :
doctor_list = Doctors_list(name="Bwire")
health_plan.save()
doctor_list.save()
#Then add the plan to the doctors list.
doctor_list.hp_id.add(health_plan)
Django creates the tabels for you. In your project folder run:
python manage.py syncdb
Health_plan and Doctors_list are both tables.
'a' and 'b' are columns in Health_plan. 'Name' and 'hp_id' are columns in Doctors_list.
Django will create a column for id in each table. Django will also create a table "Doctor_list_Health_plan" to store the relation information.
Django models are Python classes, so the Python naming conventions apply. Use HealthPlan and Doctor (CapitalizeWord singular).
Your field names are a bit abstract. I suggest you use more descriptive names. Eg:
class HealthPlan(models.Model):
name = models.CharField()
extra_care = models.BooleanField()