Django: Polymorphic foreignkey relationships - python

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.

Related

Django ManyToMany with quantity

I'm having a bit of a problem trying to model something in Django that I've conceptualized. I know that it is a many to many relationship... however it is sort of self referential and has a quantity involved. I imagine this requires a bridge model of some sort, which I have, but now how do I edit them in the admin page?
What I have is a Component class. For example, a 'screw' is a component, and it requires no further components to create it. But so is a 'housing', which requires 4 screws to hold it together. That housing could then go into a manifold and the manifold could go into a vehicle and so forth.
Each thing could potentially be a component of another thing if that makes sense. I've put all of the screws and bolts and such into the database through the admin edit page. But now I want to start putting in more complex assemblies. I could just create an Assembly class which has a list of one or more components. But I'm still left with the problem that this assembly could go into a larger assembly zero or more times.
How do I represent that?
currently I have
class ComponentBase(models.Model)
name = models.CharField(max_length=255)
class Meta:
abstract = True
ordering = ['name']
class ItemComponent(ComponentBase):
components = models.ManyToManyField('ItemComponentWithQuantity', blank=True)
class ItemComponentWithQuantity(ItemComponent):
quantity = models.IntegerField(default=1)
assuming this is the correct way to model this (is it?) how do I get the admin edit form to set this up a bit like a spreadsheet or list?
like
name: manifold assembly
components:
10x screws
10x bolts
1x assembly housing
The components field should only show the ones that have been added and the quantity. Not all possible components.
i had my model originally set up to have components = models.ManyToManyField('ItemComponent', blank=True). This caused the admin panel to have a list of all the existing ItemComponents as expected, but obviously no quantities.
Adding the ItemComponentWithQuantity class in, I changed the manytomanyfield to ItemComponentWithQuantity. But now the admin components field is empty.
I hope I'm making sense here. I'm not sure what I'm doing incorrectly.
thanks in advance.
EK
The inheritance that you're doing is making this too complicated. You can model this a different way. Change your ComponentBase to a basic model.Model (not abstract) named Assembly. Change ItemComponent to just Component. Lastly change your ItemComponentWithQuantity to AssemblyComponent.
The AssemblyComponent should have the fields
assembly - ForeignKey to Assembly
component - ForeignKey to Component
quantity - IntegerField
Make another model AssemblyAssembly with the fields
assembly_parent - ForeignKey to Assembly
assembly_child - ForeignKey to Assembly
quantity - IntegerField
You would then create either a Tabular or Stacked inline admin (depends on desired user experience) for both AssemblyComponent and AssemblyAssembly. Be sure to set fk_name in the AssemblyAssembly to point to the assembly_parent as the fk_name because you have two foreign key fields referencing the same model. You can use a raw_id_field for the assembly_child if there will be a lot of possible assemblies in the system. Use the inline admins for the Assembly admin.
For more about inline admins, see: https://docs.djangoproject.com/en/4.0/ref/contrib/admin/#inlinemodeladmin-objects
That will give you roughly the admin UI that you want with the exception that component and assembly links will be in separate inline admins.
For this you can just have a single model and make it a recursive model where it is in a relationship with itself
component = models.ForeignKey('self', on_delete=models.CASCADE)
i also advice you add an extra field for quantity instead of creating a whole model for that. Hope this works.

How can I relate some, but not all, models to users using Django?

I have a project which includes two different apps: users and school. In my current implementation, the users app contains a custom user class derived from Djagno's AbstractUser. It defines allowed user types as student or teacher, and the user must select one during registration. The school app contains a classroom model which I need to relate to students and one or more teachers.
I have two main problems here. The first is regarding how to relate the classroom model to only specific users restricting their access based on their user type. I have tried using a ManyToManyField relation in the classroom model in order to associate the instance with the user who created it (and then I would later allow the user to add students). It appears I don't understand this field correctly -- it actually all users to all classroom instances. This obviously does not meet my requirement to restrict access among different classroom instances.
OneToOneField and ForeignKey do not seem to meet my needs either. I have tried adding those fields to my user, but then I am restricted to a single classroom per user which is not acceptable either.
I have also played around with creating groups named something like "Classroom_A_teachers" and "Classroom_A_students" and then assigning permissions to them. This approach seems more complicated than necessary.
Am I overlooking something here? This is my first Django project, and I could easily have missed something basic.
Problem two is regarding adding a teacher and student model. I would like to allow a user who is registered as a teacher to be able to create a classroom instance and then to populate that classroom with student instances. Students could optionally create an account too. They would receive a classroom ID from their teacher, enter it, and their account would then be linked to the classroom instance. How can I then associate this user with an existing student model? I have toyed around with this concept with no success.
Any insight is greatly appreciated!
It appears I don't understand this field correctly -- it actually all users to all classroom instances.
It shouldn’t do that. It can just look that way in the Django Admin. The only ones actually in the ManyToMany field are the ones which are highlighted/greyed.
ManyToMany is appropriate for your use case.

What side should a django 'many-to-many' relationship reside on

Lets say I have a recipe website with two basic models, 'User' and 'Recipe'
class User(models.Model):
username= models.CharField()
email = models.CharField()
class Recipe(models.Model):
name = models.CharField()
description = models.CharField()
I would like to add the functionality so that users can 'favorite' a recipe.
In this case, I need to use a many-to-many relationship. My question is, how do I decide which model to add the relationship to?
For example, each user could have a list of 'favorite' recipes:
class User(models.Model):
favorites = models.ManyToManyField(Recipe)
Alternatively, each recipe could have a list of users who Favorited the recipe:
class Recipe(models.Model):
user_favorites = models.ManyToManyField(User)
What is considered the best practice? Is either one better for query performance?
It makes no difference from the database point of view, as pointed out in the comments.
But I have had two arguments where it did matter to me.
First (maybe less important), the built-in admin treats the two models differently by default. The model on which you define the relationship gets a widget for choosing the related objects. And a '+' for conveniently adding new objects of the related type.
secondly, you have to import one of the models in the file of the other one, if they are in different files. This matters if you want to write a reusable app that does not depend on anything outside. It mattered to me also because:
I once (well, not just once actually :)) broke my app/database/etc such, that I decided to start a new project and copy the code there. In this case you have to comment out some settings.INSTALLED_APPS to test step for step that everything works. Here it is important not to have circular includes (to include a commented-out app raises an error). So I try to import the "most basic" into the others, and not the other way round.
This not a simple answer to your question, but two points which I consider. Maybe some more experienced users can correct me if it's wrong in some sense.

django models and OOP design

I wrote a quest system for an online game. My quests are serialized into json objects for a JavaScript client that fetches those quests then from a REST backend (I use django RestFramework)
Now I'm wondering on which class or django model I should put the "behaviour" that belongs to the data.
I stored the data that belongs to a quest in several separate models:
A model QuestHistory: with models.Fields like Boolean completed, and Datetime started where I put the information belonging to a specific user (it also as a field user).
Then I have a model QuestTemplate : The part that is always the same, fields like quest_title and quest_description
I also have a model Rewards and model Task and TaskHistory that are linked to a quest with a foreign Key field.
To combine this information back to quest I created a pure python class Quest(object): and defined methods on this class like check_quest_completion. This class is the then later serialized. The Problem with this approach is that It becomes quite verbose, for example when I instantiate this class or when I define the Serializer.
Is there a python or django "shortcut" to put all fields of a django model into another class (my Quest class here), something similar to the dict.update method maybe?
Or should I try to put the methods on the models instead and get rid of the Quest class?
I have some other places in my game that look very similar to the quest system for example the inventory system so I'm hoping for a more elegant solution.
You should put the methods of the Quest class on the model itself and get rid of the Quest class.

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.

Categories

Resources