I'm working on a Django project for a medical team and have to include a calendar app to manage patients' appointments. Generally, a calendar is composed of several events with one or several attendees who are all Users. Here, attendees can be both doctors and patients. For practical and security reasons, they are separated in two different models: auth.User for medical staff, and Patient for the patients. My idea was therefore to create an abstract class called People on top of User and Patient:
class Event(models.Model):
title = models.CharField(max_length=255)
start = models.DatetimeField()
end = models.DatetimeField()
#...
class Attendee(models.Model):
event = models.ForeignKey(Event)
people = models.ForeignKey(People)
attendance = models.CharField(choices = ("Yes","No","Unknown"))
# Abstract class
class People(models.Model):
name = models.CharField(max_length=100)
#...
class Meta:
abstract = True
# Patient is a subclass of People
class Patient(People):
date_of_birth = models.CharField(max_length=100)
#...
Question is : How could I now make Auth.User also a subclass of People so that User could also be considered as attendees? Is it even possible? Otherwise how would you manage this issue?
You might want to turn things around, and inherit the medical staff model from AbstractUser and from People, like so class Staff(AbstractUser, People). This way, Staff has both the People model attributes and methods, and it still has the django User features.
Related
I have a many to many relation between two tables and relation is saved in a through table:
class Book(models.Model):
likes = models.ManyToManyField(User, through=BookLike)
class User(models.Model):
...
class BookLike(models.Model):
user = models.ForeignKey(User)
book = models.ForeignKey(Book)
When a user likes or dislikes a book I want to carry out some actions like increasing/decreasing the like count of book for example. I tried overwriting save and delete functions of BookLike but they are not called when I call book_instance.likes.remove(user) and book_instance.likes.add(user). How can I achieve this?
Wrong approach. If the Book model is connected to User, don't call users likes. Trust me that's going to bite you in the ass down the road.
So, first off:
class Book(models.Model):
users = models.ManyToManyField("User", through="BookUser", through_fields=('book', 'user'), related_name="books")
class User(models.Model):
...
class BookUser(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="bookusers")
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name="bookusers")
Then, you don't need to overwrite the default model methods. All you need to do is to register books in users or viceversa exactly how you described.
user_instance.books.add(book_instance)
Then if you want to retrieve or count how many likes a book has you can run a simple query:
book_instance.users.count()
book_instance.users.all()
or
BookUser.objects.filter(book=book_instance).count()
or the exact opposite if you want to fetch info about a specific user
Imagine there are three models named Movie, Actor, and Participation.
class Movie(models.Model):
identifier = models.CharField()
class Actor(models.Model):
name = models.CharField()
class Participation(models.Model):
movie_identifier = models.CharField()
actor = models.ForgeinKey(Actor, on_delete=models.CASCADE)
Let's assume that I can't use ForgeinKey for the movie in the Participation model.
how can I retrieve all the participation records of a movie with only one query?
Here is the solution if I had a foreign key for the movie in the participation table:
qs = Movie.objects.filter(identifier="an_identiier").prefetch_related("participations_set")
How can I do this without having a Movie foreign key in the Participation model?
Thanks!
One of the most important things when designing a database (hence when designing your models) is database normalization [Wikipedia].
You talk about Participation being related to multiple models like Movie, Series, Episode, etc. this means that Movie, Series, Episode all can be said to have something in common or they can be said to be a specialization of another entity let us say Participatable for the lack of a better word, or we can say Participatable is a generalization of Movie, Series, Episode, etc.
How do we model these? Well we will just have an extra model that our other models will have a OneToOneField with:
class Participatable(models.Model):
# Any common fields here
MOVIE = 'M'
SERIES = 'S'
TYPE_CHOICES = [
(MOVIE, 'Movie'),
(SERIES, 'Series'),
]
subject = models.CharField(max_length=1, choices=TYPE_CHOICES)
class Movie(models.Model):
# uncommon fields
participatable = models.OneToOneField(
Participatable,
on_delete=models.CASCADE,
related_name='movie',
)
class Series(models.Model):
# uncommon fields
participatable = models.OneToOneField(
Participatable,
on_delete=models.CASCADE,
related_name='series',
)
class Participation(models.Model):
participatable = models.ForgeinKey(Participatable, on_delete=models.CASCADE)
actor = models.ForgeinKey(Actor, on_delete=models.CASCADE)
Other than this solution which I find is the best for such modelling you can go with using the content-types framework which will essentially do what you do currently. That is it will use a field that stores the related id and also a foreign key that points to an entry in a table that will simply describe which table this id is for.
Well, this is the problem, I have a username column and I defined it in the models.py.
And a user may take some courses( the course may changes in the future), the relation between user and courses is 1 vs multi, which means a user can take multi courses and a courses could be taken by several users.
I am new to django and DB design, how to represent this in Django with ORM?
def user(models.Model):
name = models.CharField(max_length=30)
class courses(models.Model):
course = models.CharField(max_length=100)
Following #djsutho thought, define a many-to-many relationship:
class user(models.Model):
name = models.CharField(max_length=30)
courses = models.ManyToManyField(courses)
class courses(models.Model):
course = models.CharField(max_length=100)
Then, for example, querying courses per user would be as easy as:
courses.objects.filter(user__name='Bob')
Also note that, by Django model naming convention, model class names should start with a upper case letter: user should be User, courses - Courses.
Also note that the model name should not be in a plural form - better name courses as Course. Also better rename course field to name.
So, finally, here's the picture:
class User(models.Model):
name = models.CharField(max_length=30)
courses = models.ManyToManyField(Course)
class Course(models.Model):
name = models.CharField(max_length=100)
Hope that helps.
I am new to Django and databases and after reading the Django documentation on models I have the following question:
Let's say I have 3 models: VehicleName, CarManufacturer and TruckManufacturer. I am trying to create a database relationship where CarMaunfacturer has many VehicleNames and also TruckManufacturer has many VehicleNames. What is the relationship here and how to define it in Django? Is it as simple as define a models.ForeignKey(VehicleName) in both CarManufacturer and TruckManufacturer?
Thanks.
from django.db import models
class CarManufacturer(models.Model):
vehicle_name = models.ForeignKey(VehicleName) # IS THIS CORRECT???
# ...
pass
class TruckManufacturer(models.Model):
vehicle_name = models.ForeignKey(VehicleName) # IS THIS CORRECT???
# ...
pass
class VehicleName(models.Model):
# ...
To do exactly what you're describing:
I am trying to create a database relationship where CarMaunfacturer has many VehicleNames and also TruckManufacturer has many VehicleNames
You'd create a nullable foreign key on VehicleName to both of your Manufacturer models:
class CarManufacturer(models.Model):
# field definitions here
class TruckManufacturer(models.Model):
# field definitions here
class VehicleName(models.Model):
car_manufacturer = models.ForeignKey(CarManufacturer, blank=True, null=True)
truck_manufacturer = models.ForeignKey(TruckManufacturer, blank=True, null=True)
Then, instances of CarManufacturer or TruckManufacturer can get the names via the vehiclename_set attribute.
For a more advanced design, I would probably try to abstract the shared manufacturer behavior into a single model, then use multi-table inheritance:
class Manufacturer(models.Model):
# shared car and truck manufacturer fields go here
class CarManufacturer(Manufacturer):
# car manufacturer specific fields go here
class TruckManufacturer(Manufacturer):
# truck manufacturer specific fields go here
class VehicleName(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
See the multi-table inheritance docs for full details.
I do not think you are understanding the manufacturer to vehicle relationship property. What I think you are trying to show is that a certain Vehicle belongs to a certain manufacturer.
This type of relationship would actually be defined in the Vehicle class, as a foreign key, called manufacturer, in the Vehicle class.
In the case you are defining many vehicles under a manufacturer, you just need to rename the property to car_model or something of the like and you should be fine.
I think you have the understanding mapped out well enough. Just remember that foreign keys are only a property of one table, and say nothing about the other table itself until the relationship is established there also.
If you're working with a larger relationship, with multiple objects, you should look into using the Many-to-many field described in the django documentation.
They have an example that shows how an Articles have many Publications:
class Publication(models.Model):
title = models.CharField(max_length=30)
# On Python 3: def __str__(self):
def __unicode__(self):
return self.title
class Meta:
ordering = ('title',)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
# On Python 3: def __str__(self):
def __unicode__(self):
return self.headline
class Meta:
ordering = ('headline',)
ists,
I'm looking for some validation on a subclassing approach. I have the following:
class Person(models.Model):
"""
Basic person
"""
user = models.ForeignKey(User) # hide
first_name = models.CharField(max_length=200)
last_name = models.CharField(blank=True, max_length=200)
class Meta:
verbose_name_plural = "People"
def __unicode__(self):
return u"%s, (%s)" % (self.first_name, self.user)
class Contributor(Person):
"""
Contributor
A Core contributor of the site content workflow
"""
class Meta:
verbose_name = 'contributor'
verbose_name_plural = 'contributors'
def get_articles(self):
"""
Return the articles that the author has published.
"""
return Article.objects.filter(self_in=authors)
class Member(Person):
"""
Member
A Member of the website.
"""
# Member history, payments etc...
joined = models.DateTimeField()
So, each Member or Contributor is a Person within the system, but it is possible for a Person to be 'None', 1 or both Member & Contributor, depending on their context.
This subclassing approach makes it simple to do things like:
#...
contributors = models.ManyToManyField(Contributor, help_text="Contributors/Authors to this article")
or
print Member.objects.all()
... and of course the usual efficiencies of subclassing, i.e. common fields and methods.
However, I'm wondering about the pros & cons of doing something like
class Person(models.Model):
"""
Person
"""
user = models.ForeignKey(User) # hide
first_name = models.CharField(max_length=200)
last_name = models.CharField(blank=True, max_length=200)
is_contributor = models.BooleanField()
is_member = models.BooleanField()
but then needing to filter things like
# Assuming this is possible...
contributors = models.ManyToManyField(Person.objects.filter(is_contributor=True), help_text="Contributors/Authors to this article")
With the subclassing approach, I wonder about the challenges of being aware of users that are People (Person), Members or Contributors - and being able to discern between.
i.e. its really easy to do if person.is_contributor: but perhaps more challenging
try:
Contributor.objects.get(person__user_id=request.user.id)
except:
no_access()
else:
let_them_in()
Apologies for the open-endness of this question -- it may have been more an opportunity to think out aloud.
First, there are two oddities about your model to begin with:
1) Why is Person -=> User a ForeignKey and not a OneToOne? Might a user be more than one person?
2) User already has first and last names - why also assign them to person?
Next, to the extent that your ultimate goal is the authorization depicted at the end, why not just use permissions? Then you won't need the boolean fields or the try - except at the end.
Fundamentally, I see nothing wrong with subclassing the User model. Folks in #django often fight over this, but if done right, it is one of the most time-saving and powerful steps you can take when you first sit down with your new django project.
Adding different subclasses of User with different attributes and different methods can very quickly give you a robust user environment with enormous auth possibilities. Thus far, however, it doesn't look like you have done anything that requires you to subclass User.