Express multiple one-to-many and one-to-one relationship in Django - django

I am designing a delivery app. It has User, Address and Store models. I have following requirements:
User can have multiple delivery address
Store is located at only one location.
An address cannot be linked with both user and store.
Models looks as follow:
class User(AbstractBaseUser):
...
class Address(models.Model):
...
class Store(models.Model):
...
First requirement can be shown as:
user = models.ForeignKey(User, on_delete=models.CASCADE)
Second requirement can be shown as(in Address model as):
store = models.OneToOneField(Store, on_delete=models.CASCADE)
Also, second requirement can be shown as(in Store model as):
address = models.OneToOneField(Address, on_delete=models.CASCADE)
What is the best way to represent the third requirement. And how do I take care of serialization in this case?
Thanks.

If you center or problem on the Address model, you can handle this case during the validation:
class Address(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
store = models.OneToOneField(Store, on_delete=models.CASCADE, null=True)
def clean(self):
if self.user != None and self.store != None:
raise ValidationError('Address must belong EITHER to user OR store, not both', code='invalid')
You may also want to handle the case if both fields are None. Note that both fields need to be allowed to be null=True on database level.

Related

How to create a shared model in Django?

I have a couple of models that have need of a common set of fields. It is basically a set of various different types of contacts:
# models I would like to share
class Address(models.Model):
label = models.CharField(_('label'), max_length=50, blank=True)
street1 = models.CharField(_('street1'), max_length=125, blank=True)
street2 = models.CharField(_('street2'), max_length=125, blank=True)
city = models.CharField(_('city'), max_length=50, blank=True)
state = models.CharField(_('state'), max_length=2, blank=True)
zip_code = models.CharField(_('zip_code'), max_length=10, blank=True)
class Phone(models.Model):
label = models.CharField(_('label'), max_length=50, blank=True)
phone = models.CharField(_('phone'), max_length=50, blank=True)
# these are the models that I would like to have addresses and phone numbers
class Contact(models.Model):
name = models.CharField(_('name'), max_length=50, blank=False)
class Organization(models.Model):
name = models.CharField(_('name'), max_length=255, blank=False)
email = models.CharField(_('email'), max_length=100, blank=True)
website = models.CharField(_('website'), max_length=255, blank=True)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
photo = models.CharField(_('photo'), max_length=255, blank=True)
I would like to share the Phone and Address models with the Contact, Organization and UserProfile models. My first attempt was to add ForeignKey on each of Contact, Organization and UserProfile but after more research I believe this is backwards, so I moved the ForeignKey to Address and Phone but then discovered that ForeignKey can belong to one and only one model. In addition to sharing this data structure between multiple different contact types, I would like the ability to add more than one address or phone number to a contact. A contact could have a home address, work address, mobile number, and work number for example. So I have basically 2 questions:
1) Is sharing a model in this way a reasonable thing to do?
2) How would I go about setting up the models?
If I've understood correctly; you want your Contact, Organization, and UserProfile models to all have fields for Address and Phone. Moreover, they can each have more than one address/phone.
Is this reasonable? Sounds so to me.
How could you go about setting up the models? Generic Relations spring to my mind.
Consider just Contact and Address for now. Your second attempt is correct: we have a Many-to-one relationship (one contact, many addresses), so we need to make Contact a ForeignKey field in the Address model, like so:
class Address(models.Model):
<other_fields>
contact = models.ForeignKey('Contact', on_delete=models.CASCADE)
This allows you to assign multiple addresses to each contact.
Moving on, you essentially want your Address model to have multiple foreign keys: Contact, Organization, and UserProfile. One way of achieving this is to use Generic Relations. This makes use of Django's built-in "contenttypes" framework, and allows you to create GenericForeignKey fields that point to more than one model. I encourage you to read the docs linked, since generic relations aren't so trivial. In the end, you'll have something like:
class Address(models.Model):
label = models.CharField(max_length=50, blank=True)
street_1 = models.CharField(max_length=125, blank=True)
street_2 = models.CharField(max_length=125, blank=True)
etc...
models_with_address = models.Q(app_label='app_name', model='contact') | \
models.Q(app_label='app_name', model='organization') | \
models.Q(app_label='app_name', model='userprofile')
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, limit_choices_to=models_with_address)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey()
With this, you can create several addresses for each of the models specified in the models_with_address query. To be able to query the addresses for a given contact/organization/etc., you'll need a reverse generic relation. Setting this up involves adding the line address = GenericRelation(Address) to the respective models.
For further generalisation, you could create a ContactableModel (or whatever) class:
class ContactableModel(models.Model):
address = GenericRelation('Address')
phone = GenericRelation('Phone')
Any model with an address and phone number (Contact, Organization, etc.) could then inherit this so that you don't have to repeatedly include those two fields. You could also improve the models_with_address limit, so that we have something like limit_choices_to=<subclasses_of_ContactableModel>.
Hope this helps!
A simple solution would be to add ManyToManyField(Address|Phone) (i.e. two M2M fields)
to Contact, Organization and UserProfile.
But that would mean different contacts/organizations/users could share addresses.
While this looks tempting, the problem with such a solution is that if someone
edits an address of a contact, they also change the address for all the remaining objects in the system!
Another possible solution which avoids the above problem
and doesn't require M2M fields or generic relationships would be to use
multi-table inheritance:
class Address(models.Model):
entity = models.ForeignKey(Entity)
...
class Entity(models.Model):
pass
class Contact(Entity):
...
class Organization(Entity):
...
(Under the hood Django creates implicit OneToOneField-s
that point from Contact & Organization to Entity.)

How To? django model field whose value is a choice of a manytomany field of the same model

I have a django model called company with a manytomany field where company members are added.
I have another field, called 'company_contact' where I want to be able to choose from one of the company_members as if it was a ForeingKey to company_members.
Is there an easy way of doing this without customized forms, ajax request, django-autocomplete-light, etc?
I intend to fill this model using django admin.
Thanks
class Dm_Company(models.Model):
company_name = models.CharField(max_length=80, blank=True, verbose_name="Razon Social")
company_members = models.ManyToManyField(conf_settings.AUTH_USER_MODEL, verbose_name="Miembros")
#company_contact = models.ForeignKey(conf_settings.AUTH_USER_MODEL, related_name="company_members", on_delete=models.CASCADE)
company_phone = models.CharField(max_length=80, blank=True, verbose_name="Telefono compania")
company_email = models.CharField(max_length=80, blank=True, verbose_name="Email compania")
The one way that I can think of would be to use a ManyToMany with a through model.
class Dm_Company(models.Model):
company_name = models.CharField(max_length=80, blank=True, verbose_name="Razon Social")
company_members = models.ManyToManyField(conf_settings.AUTH_USER_MODEL, through='CompanyMembership')
...
class CompanyMembership(models.Model):
company = models.ForeignKey(Dm_Company)
user = models.ForeignKey(conf_settings.AUTH_USER_MODEL)
is_contact = models.BooleanField(default=False)
The difficulty with this model is that you need to write logic to prevent more than one CompanyMember from being set as is_contact. However, it does structure your data model such that there's no way for the company_contact to reference a user in a different company.
There is no way to filter the company_contact queryset in the way you describe. An alternative is to add the following to your model:
def clean_fields(self, exclude=None):
super().clean_fields(exclude=exclude)
if not self.company_members.exists(id=self.company_contact_id):
raise ValidationError('contact is not member')
That will prevent a contact being selected that is not a member

Store List(s) in a database

My Users has phone contact LIST(s) - [3121234567,2121234567,6601234567]
Now, I want each user to be able to store as many LIST as possible. Each List must have a name(or description) attached to them under each USER account. Note: the number of LIST is dependent on Users needs. Example:
Students
[3121234567,2121234567,6601234567]
Club Member
[8101234567,8151234567,8171234567]
Now, how do I store it in a database.
Django User Model
class CustomUser(AbstractBaseUser):
username = models.CharField(max_length=254, unique=True)
first_name = models.CharField(max_length=24)
last_name = models.CharField(max_length=30)
phone = models.CharField(max_length=10)
email = models.EmailField(max_length=128)
street = models.CharField(max_length=128)
city = models.CharField(max_length=128)
state = models.CharField(max_length=2, choices=STATE_CHOICES, default=STATE)
zip_code = models.IntegerField(max_length=5, null=True, blank=True
USERNAME_FIELD = 'email'
REQUIRED_FIELD = ['username', 'first_name', 'last_name', 'phone', 'street', 'city', 'state']
objects = CustomUserManager()
Edit(Added):
I am not looking to create Student or ClubMember models. This name is use to identify the python phone contact list. PhoneAddress one can be labelled(named) as Student for one user but called "Conference Attendant" for another. Each user have different # of Lists.
PhoneAdress PhoneAdress2 PhoneAdress3
[3121234567,2121234567,6601234567] [4121234567,3121234567,6601234567] [7121234567,8121234567,9601234567]
Lemme guess, you're coming from a NoSQL background where the database is a document in a JSON form?
If so, I am sorry, in a Relational Database, used by Django in the likes of PostgreSQL or MySQL, they call something Foreign Keys, and that is your way of storing multiple "Lists" related to a particular field.
If you want many users to store as many lists as possible, you're looking at something like this, roughly speaking:
class myUserModel(models.Model):
# your arbitrary fields here
# then you do something like this:
class Student(models.Model):
user = models.ForeignKey(User)
class clubMember(models.Model):
user = models.ForeignKey(User)
With the above setup, you can add as many Student objects associated to the myUserModel class, so as the clubMember
However, if you wish to use PostgreSQL specifically, as your backend (perhaps as perpetual storage backend), you might find some sense in using Django's support for the ArrayField
And ooh, you might need to extend the Django User Model to add any extra fields easily, unless you're willing to go down the road of a custom User Model.
More info:
Django ForeignKey
This SO answer on 'OneToManyFields', similar to adding multiple items to a single field.
I hope the above helps
Create some models:
class Club(models.Model):
name = models.CharField(max_length=256,blank=True,null=True)
date_open = models.DateField()
class Institution(models.Model):
name = models.CharField(max_length=256,blank=True,null=True)
address = models.CharField(max_length=256,blank=True,null=True)
type = models.CharField(max_length=256,blank=True,null=True) #university, college
Rather than using
class CustomUser(AbstractBaseUser):
username = models.CharField(max_length=254, unique=True)
first_name = models.CharField(max_length=24)
last_name = models.CharField(max_length=30)
use composition in the form of OneOnOneField
class UserProfile(models.Model):
user = models.OneOnOneField(User,blank=True,null=True)
club_member = models.ManyToManyField(Club,blank=True, null=True)
institutions = models.ManyToManyField(Institution,blank=True, null=True) # student in
Once you have this, you will be able to get and add as many institutions and clubs to the lists:
user = User.objects.get(id=user_id)
club = Club.objects.get(id=club_id)
institution = Institution.objects.get(id=institution_id)
user.profile.clubs.add(club)
user.profile.institutions.add(institution)
So to verify if the user is a member of a club
club = user.proile.clubs.get(id=club_id)
and to verify the user is a student in an institution use
institution = user.profile.institutions.get(id=institution_id)

Django Multiple User Profiles with Class based views (Best Practices)

I have a website where there are two kinds of users (say) : students and tutors.
Both types have common login functionality, age, gender etc but have distinct attributes such as report cards for students and degree certificates for tutors.
I read this question : Django - Multiple User Profiles and setup my profiles as shown below:
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key=True, related_name='profile')
mobile = models.CharField(max_length=10, blank=False, null=True)
picture = models.ImageField(
upload_to='images/', default='images/newuser.png')
age = models.IntegerField(null=True)
slug = models.SlugField()
...
And two other models that link to the above. Eg:
class StudentProfile(models.Model):
profile = models.ForeignKey(UserProfile, related_name="user_profile")
#more custom attributes
class TutorProfile(models.Model):
profile = models.ForeignKey(UserProfile, related_name="doctor_profile")
#more custom attributes
Now my questions:
1) SlugField is defined on the UserProfile attribute but will ideally use the User.username field. This means a join between these two tables will happen each time. Is this to be expected?
2) Assuming I am using class based views, editing/viewing the profile will depend on the UserProfile in question. But I want the user to be able to edit/view all his details on the same page. Thus, I will have to fetch TutorProfile / StudentProfile too and add custom logic to ensure updates happen to them too.
It seems to me that there should be a proper way of handling these situations (Since a lot of websites have similar requirements). What are the best practices to be followed in such a situation?
While searching for answers, I came across a solution which I think might suit my needs (posting here to welcome critique and help out others who might be looking for answers).
Taken from Django Design patterns and Best Practices
class UserProfile(models.Model):
user = models.OneToOneField(User, primary_key=True, related_name='profile')
mobile = models.CharField(max_length=10, blank=False, null=True)
picture = models.ImageField(
upload_to='images/', default='images/newuser.png')
age = models.IntegerField(null=True)
gender = models.IntegerField(null=True)
user_type = models.CharField(max_length=20, choices=UserTypes.CHOICES)
slg = models.SlugField()
class Meta(object):
abstract = True
class StudentProfile(models.Model):
report_card = models.FileField(upload_to="/temp")
house = models.CharField()
class Meta(object):
abstract = True
class TutorProfile(models.Model):
performance = models.IntegerField(default=0)
salary = models.IntegerField(default=0)
class Meta(object):
abstract = True
One base abstract class and two specific classes which cover the various user profiles. Keeping them separate like this makes it easy for us to reason about the various fields present in each user type.
Finally,
class Profile(UserProfile, StudentProfile, TutorProfile):
pass
This is the model used as the settings.AUTH_USER_MODEL.
Overall, the advantages I see:
Single DB call on user edit/view page.
Easier to think about overall.
Disadvantage : Lots of wasted space.
Anyone has any better suggestions?

Django Multi-Table Inheritance VS Specifying Explicit OneToOne Relationship in Models

Hope all this makes sense :) I'll clarify via comments if necessary. Also, I am experimenting using bold text in this question, and will edit it out if I (or you) find it distracting. With that out of the way...
Using django.contrib.auth gives us User and Group, among other useful things that I can't do without (like basic messaging).
In my app I have several different types of users. A user can be of only one type. That would easily be handled by groups, with a little extra care. However, these different users are related to each other in hierarchies / relationships.
Let's take a look at these users: -
Principals - "top level" users
Administrators - each administrator reports to a Principal
Coordinators - each coordinator reports to an Administrator
Apart from these there are other user types that are not directly related, but may get related later on. For example, "Company" is another type of user, and can have various "Products", and products may be supervised by a "Coordinator". "Buyer" is another kind of user that may buy products.
Now all these users have various other attributes, some of which are common to all types of users and some of which are distinct only to one user type. For example, all types of users have to have an address. On the other hand, only the Principal user belongs to a "BranchOffice".
Another point, which was stated above, is that a User can only ever be of one type.
The app also needs to keep track of who created and/or modified Principals, Administrators, Coordinators, Companies, Products etc. (So that's two more links to the User model.)
In this scenario, is it a good idea to use Django's multi-table inheritance as follows: -
from django.contrib.auth.models import User
class Principal(User):
#
#
#
branchoffice = models.ForeignKey(BranchOffice)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
#
#
#
Or should I go about doing it like this: -
class Principal(models.Model):
#
#
#
user = models.OneToOneField(User, blank=True)
branchoffice = models.ForeignKey(BranchOffice)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="principalmodifier")
#
#
#
Please keep in mind that there are other user types that are related via foreign keys, for example: -
class Administrator(models.Model):
#
#
#
principal = models.ForeignKey(Principal, help_text="The supervising principal for this Administrator")
user = models.OneToOneField(User, blank=True)
province = models.ForeignKey( Province)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratorcreator")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="administratormodifier")
I am aware that Django does use a one-to-one relationship for multi-table inheritance behind the scenes. I am just not qualified enough to decide which is a more sound approach.
I'd like to expand on the solution by #thornomad.
Extending Django's User class directly can cause all kinds of trouble with the internal django.auth mechanisms. What I've done in a similar situation is precisely what #thornomad suggests - I made my own UserProfile model linked one-to-one with the Django User model, in which I held additional user data and from which I inherited models for different types of users.
Something to fit what you described:
class UserProfile(models.Model):
user = models.OneToOneField(User, blank=True, related_name='profile')
class Meta:
abstract = True
class PositionHolderUserProfile(UserProfile):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="created_users")
modified_by = models.ForeignKey(PositionHolderUserProfile, editable=False, blank=True, related_name="modified_users")
class Principal(PositionHolderUserProfile):
branchoffice = models.ForeignKey(BranchOffice)
class Administrator(PositionHolderUserProfile):
superior = models.ForeignKey(Principal, related_name="subordinates")
province = models.ForeignKey(Province)
class Coordinator(PositionHolderUserProfile):
superior = models.ForeignKey(Administrator, related_name="subordinates")
class Company(UserProfile):
name = models.CharField(max_length=50)
class Product(models.Model):
name = models.CharField(max_length=50)
produced_by = models.ForeignKey(Company)
class Buyer(UserProfile):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
products_bought = models.ManyToManyField(Product)
I recently switched over to using models that inherit off of contrib.auto.models.User. My general observation is that in theory they're great, but sometimes they don't get auto-magically handled like they're supposed to.
I think your decision regarding inheritance vs. OneToOne comes down to this:
Do I want to have Django automatically do something right 95% of the time, and need to debug that other 5%
-OR-
Do I want to do something manually myself 100% of the time
If you haven't seen it, the Scott Barham blog has a great post about inheriting off of User, and also building a custom back end to make sure that your custom object is being returned -- Extending the Django User.
Additionally of interest would be the AutoOneToOne field provided by django-annoying. It's sort of a hybrid of the two approaches here -- there's no inheritance taking place, but Django is taking care of creating the matching OneToOneField if it's not present.
Also, thornomad does make a good point about the redundancy in your models. You could easily implement an abstract class to clean that up as so (assuming you're doing manual OneToOne):
class BaseExtendedUser(models.Model):
user = models.OneToOneField(User, blank=True, related_name='profile')
landline = models.CharField(blank=True, max_length=20)
mobile = models.CharField(blank=True, max_length=20)
created_by = models.ForeignKey(User, editable=False, blank=True, related_name="created_users")
modified_by = models.ForeignKey(User, editable=False, blank=True, related_name="modified_users")
class Meta:
abstract = True
class Administrator(BaseExtendedUser):
province = models.ForeignKey(Province)
class Principal(BaseExtendedUser):
branchoffice = models.ForeignKey(BranchOffice)
I don't think I would inherit the User model, rather use a custom UserProfile - leaving the contrib.auth model alone. With the custom UserProfile model, you could setup a base user profile model that can be a part of all your different user types.
Just looking at it quickly, too, I would look carefully at any models that repeat all the same fields (like your last two Principle and Administrator models). Combining the built in group functionality with the user profile idea may do what you are looking for.
Please consider what happens in the data model when a Coordinator gets promoted to a Principal. I would not use inheritance in this case at all. Please reconsider the previous poster's suggestion "Combining the built in group functionality with the user profile idea may do what you are looking for."
Do you need objects of your user classes to act like an auth.User anywhere? That would be the most obvious reason to use inheritance over OneToOne. One pro of the OneToOne approach would be the ease in which you can change over to another User model, if that's a concern.
The real issue I see with what you have above (by either method) is that there doesn't appear to be anything stopping you from having a Principal object and an Administrator object share the same User. OneToOneField can only guarantee a one-to-one mapping between any two relations.