I have an Abstract model:
class Distributor(models.Model):
class Meta:
abstract = True
and 2 models which inherit it:
class DistributorGroup(Distributor):
pass
class DistributorPerson(Distributor):
pass
also I have a llink model:
class Link(models.Model):
distributors_persons = models.ManyToManyField(
'people.DistributorPerson', blank=True, related_name='distributors_persons_of_links')
distributors_groups = models.ManyToManyField(
'groups.DistributorGroup', blank=True, related_name='distributors_groups_of_links')
A link can have a relation with one of the distributors. I accomplished this behavior by adding 2 m2m and setting blank=True to both.
Now I realized that I need a through model to connect Distributors and Link. But as through model can't have an abstract model as a foreign key I don't know what to do. Do I need to create 2 separate through models for DistributorPerson and DistributorGroup or there is a way to accomplish this with 1 through model. Also I don't know if my 2 m2m in Link model are a proper way to accomplish the behavior that I want.
So I would like to know what is the way to organize these m2m models with an abstract model.
The first question is whether DistributorPerson and DistributorGroup really need to be separate tables. If they're very similar it might make sense to just use a single table. (For example, if you were modeling phone numbers you probably wouldn't use separate Home, Work, and Mobile tables, you'd instead use a single table with a type field.)
(And note that you can use proxy models to allow different Django models to share the same database table.)
If you do need separate tables, then you might look at GenericForeignKey. This is a mechanism to allow foreign keys to reference objects from different model types. In your case this might look like:
class DistributorGroup(Distributor):
distributor_links = GenericRelation(DistributorLink, related_query_name="distributor_groups")
class DistributorPerson(Distributor):
distributor_links = GenericRelation(DistributorLink, related_query_name="distributor_persons")
class Link(models.Model):
pass
class DistributorLink(models.Model):
link = models.ForeignKey(Link);
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
See the documentation on generic relations for examples and more detail.
Finally, if all else fails, you could indeed create two separate M2M tables for the two kinds of relation.
Note that none of this really has anything to do with abstract models. Abstract models are just a mechanism for code reuse in Django, they don't affect the tables or the queries you run on them.
Related
I have CustomUser model and Post model. I consider adding a lightweight like mechanism to the posts.
What comes to my mind is defining a Like model in such fashion to connect the models to each other:
class LikeFeedback(models.Model):
likingUser = models.ForeignKey(CustomUser)
post_liked = models.ManyToManyField(Post)
But this design produces a new row in the database with each like.
Another option is to define CustomUser and Post models in a way that:
class Post(models.Model):
...
users_liked = models.ManyToManyField(CustomUser)
class CustomUser(models.Model):
...
posts_liked = models.ManyToManyField(Post)
I am not sure if this approach creates a new row or uses a different indexing mechanism, but it looks tidier.
In terms of DB performance what approach is the fastest? Do I need to define the ManyToMany connection in both models to speed up DB processes? Because 15 posts are to be displayed on the webpage at once and and with every post it is necessary to check if the visitor already liked the note. Also, with each like and takeback a write operation is to be performed on the DB.
I am not sure if this approach creates a new row or uses a different indexing mechanism, but it looks tidier.
A ManyToManyField will create an extra table called a junction table [wiki] with ForeignKeys to the model where you define the ManyToManyField, and the model that you target with the ManyToManyField.
You furthermore only need one ManyToManyField, otherwise you make two relations that act indepdently. You thus model this as:
from django.conf import settings
class Post(models.Model):
# ...
likes = models.ManyToManyField(
settings.AUTH_USER_MODEL,
related_name='liked_posts'
)
class CustomUser(models.Model):
# ...
# no ManyToManyField to Post
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Let us say in my models for a django app, I have three models, each with many to many relationship (this is a toy example).
class User(models.Model):
permissions_group = models.ManyToManyField(Group)
class Group(models.Model):
permissions_area = models.ManyToManyField(Area)
class Area(models.Model):
#other irrelevant field data...
pass
I would like to have a field on my User model, that expressed the relationship between users and Areas, which is an implicit many-to-many model (that is to say I don't define additional relations which create additional tables in the database, I use the relationship which goes through groups).
I have considered using a custom manager, but that doesn't seem to allow the kind of relationship filtering that one sees with a standard RelatedField manager; I could simply set a decorated property on the class:
class User(models.Model):
#property
permissions_areas(self):
return Area.objects.filter(group__in=self.permissions_groups.all())
But that seems clunky, and doesn't use any django conventions. Is there a conventional way to do this in django using Django's tooling (custom managers or something similar to RelatedManager) which I am missing?
You can just use two underscores to look through a relation, so:
class User(models.Model):
#property
permissions_areas(self):
return Area.objects.filter(group__user=self).distinct()
The .distinct() is useful if a User can belong to multiple groups that have access to this area. Without .distinct() it would return that Area for each group that has permission to that area and where the user belongs to.
I want to insert a many-to-many relationship into some models, without having to completely rewrite the models.
For example, consider the django User model and a model Foo from a third-party module I've installed.
If I 'owned' Foo I could just do:
class Foo(models.Model):
users = models.ManyToMany(User)
Then, if I wanted to add a Foo to user, or vice versa I could do:
my_user.foo_set.add(my_foo)
my_foo.users.add(my_user)
But I don't 'own' either code, and I want to inject this relationship, so I can do the above.
Now, if I wanted I could even do a through relationship through a model I made and and put that on either side, but that still requires altering the models.
Now, behind the scenes it looks like django many-to-many relationships are models (they definitely have tables), is it possible to make this code:
class FooUserRelationship(models.Model):
foo = ForeignKey(Foo)
user = ForeignKey(User)
Act just like a many-to-many relationship?
you can use proxy models. Here person is a 3rd party model and Myperson is a your model modifying extra attributes to the main models. for more details https://docs.djangoproject.com/en/1.9/topics/db/models/#proxy-models
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
class MyPerson(Person):
users = models.ManyToMany(User)
class Meta:
proxy = True
The cleanest way i can think of is to provide a middle class (or you could subclass which would provide the almost the same queries) with a onetoone to one side and then many to many
class FooUser(models.Model):
user = OneToOneField(AUTH_USER_MODEL)
foo = ManyToMany(Foo)
my_user.foouser.foo_set.add(my_foo)
my_foo.foousers.add(my_user.foouser)
Now I admit that this isn't the cleanest way of doing things since it would involve a further SQL join when retrieving results, but it does keep in tact your link and provides a way to modify as you please.
Let's say i have several different models in django project. Now i need to implement reddit-like rating system, that can be easily added to any model in the project. Model look like this:
class Rating(models.Model):
vote = models.IntegerField(blank=False, null=False)
user = models.ForeignKey(User)
The question is - how to connect this 'abstract' model to any other model in project?
You need generic relations from built-in contenttypes framework.
First (and with the disclaimer that I don't know anything about Reddits rating system), unless you want users to put in ratings from negative millions to positive millions, why not use choices?
Now to the problem at hand: A model that is rate-able should have a relation that allows many ratings to one model. Unfortunately this means that none of the relationship fields in Django will work. The absolute simplest solution to this is to have the rating in the actual model.
If you don't want to copy-paste the rating fields you can use an abstract model and inherit from that:
class RateableModel(models.Model):
rating = models.Integerfield(...)
rating_user = models.ForeignKey(User)
class Meta:
abstract = True
class SomeModelThatCanBeRated(RateableModel):
# Fields for this model
I'm attempting to have inherited class templates, so that all my models have certain default fields, and all have default overrides for a few functions like save_model()
If I do it like this, I get the overrides, but then have to go and manually set meta data like db_table...
class ModelExtension(models.Model):
altered_by = models.CharField(max_length=64)
class SomeModel(ModelExtension):
class Meta:
db_table = 'app_somemodel'
fields = models.CharField()
...
Is there a way to get this kind of inheritance working right? So far I either have to do extra work to compensate for the drawbacks of this approach, or I'm plagued by MRO errors.2
What's an MRO error? Have you read the django docs on model inheritance? You can either have Abstract Base Classes, Multi-table inheritance, or proxy models.
http://docs.djangoproject.com/en/stable/topics/db/models/#abstract-base-classes
What you've done there is a multi-table inheritance - there's a hidden OneToOneField connecting your two models. I don't know why you think you need the db_table specified - it shouldn't be.
If you are never going to have objects of bare class ModelExtension then you want abstract base classes. In this case you put abstract=True in the Meta section, and then all the fields from the base class are added to the table for the child class. The docs explain it better than I can here.
I often find myself starting to do it one way and then flipping back and forth several times as I think more about my database structure....
If you just want to add new functionality to a model without changing its fields, use a proxy model!