Django - repeat ForeignKey vs. multi-level relationship - python

Assuming below (simplified) database schema:
class Site(models.Model):
...
class Business(models.Model):
site = models.ForeignKey(Site)
class Subscription(models.Model):
business = models.ForeignKey(Business)
class Invoice(models.Model):
subscription = models.ForeignKey(Subscription)
Is it better to include site = models.ForeignKey(Site) field in every site-related model (all above) and then ie.:
invoice = Invoice.objects.get(id=1)
invoice_site = invoice.site
or use multi-level relation like this:
invoice = Invoice.objects.get(id=1)
invoice_site = invoice.subscription.business.site
I have such situations in many places of my code and always wonder which option is better. First is cleaner, do not break "DRY" rules and prevents inconsistency but the other allows to gain performance and optimization improvements through preventing complicated database joins.
Of course I assume that site is always consistent in above models, so there is no possibility to have Business with site A and Invoice with site B.

Well, relational database design has no place for DRY, it's all about relations. You need to think logically what the relationship structure of your application should be.
I couldn't speak definitively because I'm not in your shoes to understand the requirement 100%, but I'm almost certain that site shouldn't be directly associated with Invoice. Invoice is related to your business subscriptions and shouldn't depend on/know which site does the business happen. Same thing for Subscription model. A simple trouble if you have site in Invoice is that you are duplicating the relationship, and an Invoice couldn't exist if there's no such site yet.
I suggest you check out some basics of relational database design and gain a better understanding of the concept of normalization. It would help you design a better schema without causing trouble with relations.

Related

When to use each model relationship in Django?

I've been reading through the Django documentation and looking over some of the other answers on the site for a couple of hours now, yet I still can't get it to sink in. I know this isn't Django specific, but the examples I use will be from a Django project.
My question boils down to when is it appropriate to use each:
Many-to-many relationships
Many-to-one relationships
One-to-one relationships
One-to-one, more or less makes sense to me.
Now for the other two. While I understand the differences between them in isolation, when it comes to using them practically in a project, I get confused. Here is an example:
class User(AbstractUser):
pass
class Listing(models.Model):
title = models.CharField(max_length=64)
description = models.TextField()
class Watchlist(models.Model):
user = models.ForeignKey(User, related_name='watchlist', on_delete=models.CASCADE)
item = models.ManyToManyField(Listing)
class Comment(models.Model):
user = models.ForeignKey(User, related_name='comments', on_delete=models.SET_NULL)
comment = models.TextField()
Would this be the correct use of Many-to-one(ForeignKey) and Many-to-many?
Should Watchlist.item be a ForeignKey? Or is M2M correct?
Wouldn't it simplify to make the 'Watchlist' part of the User class? (give them an empty list to populate with listing ID's)
Why is Watchlist.user not a One-to-one relationship, if each watchlist belongs to a single user, and a user can only have one list?
Apologies for my stupidity, I just can't get this to sink in!
Thank you.
edit: Context, the models are from a 'learning' project I was working on intended to be an auction site, similar to eBay. The watchlist is sort of a 'wish' list... for the user to watch an item, not for site to watch a user!
To explain it simply these django-models or objects represents tables in your database and the fields are like the columns in them. So with a one-to-one relation you can only have one row in one table relating to one row in another table. For example one user in the user table (represented by one row) can only relate to one row in a profile table. But your user can have many comments, so this would be a one-to-many/foreignkey relation (if you set unique=true on a fk, it will in practice function as 1:1). If the users can collaborate on writing comments, or for example as here on stackoverflow where users can edit other users comments, that would be a many-to-many relation.
Database design can be complicated/complex, especially using an ORM without basic knowledge of SQL and how it all works beneath. In general it requires a bit of planning even for a simple application.

Should I be using a ForeignKey in this Django model?

I've been reading a lot about Django and have finally begun developing the models for a project I've outlined, but I'm having trouble understanding ForeignKeys, and how/when to use them. I have read a bit about database work to better understand Foreign keys in general, but with no prior database experience I am still kind of confused.
I've come up with an example situation, similar to the project I'm working on and would like to know if both uses of the ForeignKey are proper, or should I use a OneToOneField or ManyToManyField in their place.
In this example situation, I would like each Bridge to have multiple builders, and each tool to have multiple different users (or Builders who are skilled with them). I would NOT like a builder to work on multiple bridges at the same time (OneToOne??). However, I would like each builder to have the ability to have multiple Tools they are skilled with(ManyToMany??).
class Builder(models.Model):
first_name = models.CharField( max_length = 50 )
class Bridge(models.Model):
bridge_name = models.CharField( max_length = 50 )
builders = models.ForeignKey(Builder)
class Tool(models.Model):
tool_name = models.CharField( max_length = 50 )
users = models.ForeignKey( Builder )
From my understanding, I can retrieve all of a builders' tools in the views.py file, although I haven't gotten to this part of the development yet, and would like to ensure I have a strong model before getting that far.
Considering how generic and open-ended this question can be, overall I would just like someone to explain:
WHY each ForeignKey relationship in this example
model is either used correctly or incorrectly.
It's not actually a Django question but rather a ORM question, you need to understand what and how ORM works (apart from the language and the framework used).
Your case yields different results:
1 Bridge can Have Many workers (One (bridge) <--* Many (Worker), ForeignKey)
1 Bridge can have 1 worker (One (Bridge) <--> One (Worker), One To One field)
Many Bridges can have many workers (Many (Bridges) * -- * many (Workers), Many to Many field)
If you use a ForeignKey from tools to builders, it means that a worker can have many tools, but each tool is unique to a worker, if you have many to many then any worker can have any tool.
If you need tools to belong to many different workers, then you need a many to many relation, if the tools need some specific restrictions (like skills) then again a many to many relation with specific limitations (a through table could hold extra information regarding the association, like worker 1 uses tool 1 every friday, that would be a through table of workers to tools with the middle table holding the day the worker uses the specific tool).

Django many-to-many generic relationship

I think I need to create a 'many-to-many generic relationship'.
I have two types of Participants:
class MemberParticipant(AbstractParticipant):
class Meta:
app_label = 'participants'
class FriendParticipant(AbstractParticipant):
"""
Abstract participant common information shared for all rewards.
"""
pass
These Participants can have 1 or more rewards of 2 different kinds (rewards model is from another app):
class SingleVoucherReward(AbstractReward):
"""
Single-use coupons are coupon codes that can only be used once
"""
pass
class MultiVoucherReward(AbstractReward):
"""
A multi-use coupon code is a coupon code that can be used unlimited times.
"""
So now I need to link these all up. This is how I was thinking of creating the relationship (see below) would this work, any issues you see?
Proposed linking model below:
class ParticipantReward(models.Model):
participant_content_type = models.ForeignKey(ContentType, editable=False,
related_name='%(app_label)s_%(class)s_as_participant',
)
participant_object_id = models.PositiveIntegerField()
participant = generic.GenericForeignKey('participant_content_type', 'participant_object_id')
reward_content_type = models.ForeignKey(ContentType, editable=False,
related_name='%(app_label)s_%(class)s_as_reward',
)
reward_object_id = models.PositiveIntegerField()
reward = generic.GenericForeignKey('reward_content_type', 'reward_object_id')
Note: I'm using Django 1.6
Your approach is exactly the right way to do it given your existing tables. While there's nothing official (this discussion, involving a core developer in 2007, appears not to have gone anywhere), I did find this blog post which takes the same approach (and offers it in a third-party library), and there's also a popular answer here which is similar, except only one side of the relationship is generic.
I'd say the reason this functionality has never made it into django's trunk is that while it's a rare requirement, it's fairly easy to implement using the existing tools. Also, the chance of wanting a custom "through" table is probably quite high so most end-user implementations are going to involve a bit of custom code anyway.
The only other potentially simpler approach would be to have base Participant and Reward models, with the ManyToMany relationship between those, and then use multi-table inheritance to extend these models as Member/Friend etc.
Ultimately, you'll just need to weigh up the complexity of a generic relation versus that of having your object's data spread across two models.
Late reply, but I found this conversation when looking for a way to implement generic m2m relations and felt my 2 cents would be helpful for future googlers.
As Greg says, the approach you chose is a good way to do it.
However, I would not qualify generic many to many as 'easy to implement using existing tools' when you want to use features such as reverse relations or prefetching.
The 3rd party app django-genericm2m is nice but has several shortcomings in my opinion (the fact that the 'through' objects are all in the same database table by default and that you don't have 'add' / 'remove' methods - and therefore bulk add/remove).
With that in view, because I needed something to implement generic many-to-many relations 'the django way' and also because I wanted to learn a little bit about django internals, I recently released django-gm2m. It has a very similar API to django's built-in GenericForeignKey and ManyToManyField (with prefetching, through models ...) and adds deletion behavior customisation. The only thing it lacks for the moment is a suitable django admin interface.

The right way to design a job model

I have a silly Job model in my app:
class Job(models.Model):
# working info
user = models.ForeignKey(User)
company = models.CharField(max_length=100, blank=True)
department = models.CharField(max_length=100, blank=True)
inaugural_date = models.DateField('inaugural date', blank=True)
resignation_date = models.DateField('resignation date', blank=True)
the above model is exactly what I have in my app and this model is used to let user get their college, nothing more.
It works, but I think there must be a better way to design this model, because suppose I want get all the users within the same company and same department in same period of time, its not that easy.
Please help me to reconstruct this model, any suggestion will be appreciated!
This should work:
User.objects.filter(
job_set__company__iexact="wwwww",
job_set__department__iexact="xxxxx",
job_set__inaugural_date__gt=yyyy,
job_set__resignation_date__lt=zzzz
)
It doesn't seem that complex to me, such query wouldn't be easy if you think in SQL terms, at least for me :)
Depends how much you predict on growing and what backend you use. For relation db (mysql/postgres etc) I would create a separate model for departments and companies and also create join tables for them in order to make my job easier later on.
Your model looks fine.
Even though Django model is not Anemic , I usually look it as pure data structure without too much logic. So I think
get all the users within the same company and same department in same period of time
is a logic which should not be a concern of your model, which also means, it should not influence your model design too much IMHO.
Model should be kept simple and thin, so that it can be adopted and adapted in/to different kinds of logic. If you try to make you model structure suitable to a specific logic, you will easily find yourself entangled with inflating models.
BTW, for complex query, you can fallback to Raw SQL

Django: Polymorphic foreignkey relationships

I'm developing kind of a social network in Django, but I don't see the way to maintain coupled my models.
I have a model User, which can comment on other Users, Pics, Pages (capitals are because they're models) via a Comment model. In order to arrange all that comments I created the Board model, that has one owner and receives all the comments addresed to that owner. The problem is I don't know how is its owner, i.e., which model does it belong to. It could be a pic's board, but also a user's one. So I end up with a model like this:
class Board(models.Model):
category = models.IntegerField()
owner_id = models.IntegerField()
I store owner's id and owner's category to be able to perform other tasks, but this solution doesn't convince me at all. A friend has advised me to use an abstract class Board, and then create a UserBoard, PicBoard, PageBoard which inherit from Board, but I think that will be useless too. I've thought about GenericRelations, but I'm not sure if that is the correct way to achieve what I'm trying. Any proposal will be listened and considered, thanks for your help :)
PS: Oh, and sorry if the title isn't very descriptive, but I couldn't figure out a better one. It's hard to explain this problem
Option 1
Create a class BoardOwner. Have all models which have a board inherit from that, and have the board have a foreignkey relationship with BoardOwner. You'll need to then scan over the various childlinks to figure out which one is the "real" child. This more fiddly, but localises all of the complexity in one class.
Option 2
Have a foreignkey from each class that has a board to the board. If you always have boards enter your system via a method on the owner (as opposed to a query on Board or elsewhere), you can have code in the owner which lets the Board know which foreign key reverse relationship to use. You'll probably want to factor that code into its own class.
Option 3
Use contenttypes: https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/
This wraps up this sort of thing.

Categories

Resources