I'm working on a table consisting of all events performed on a user despite that these events are represented by 4 different models.
The models are:
WebhookSubscription
WebhookEvent
TokenRefresh
LogEvent
I want these to all be visible to the user in their dashboard, but in a single table ordered by timestamp. I could do a single query for each model then append to a dictionary which I'll then sort by date, however, I don't want to go against a native solution if there is one (and I don't believe this is an edge case so I could foresee it).
Is there a native way of retrieving multiple models in a single QuerySet?
There is no native way to retrieve multiple un-related models in a single QuerySet. What you can do is to use model inheritance, have a base model for all event models. With the following model structure;
class Event(models.Model):
timestamp = models.DateTimeField()
user = models.ForeignKey(User)
...
class WebhookSubscription(Event):
...
class WebhookEvent(Event):
...
class TokenRefresh(Event):
...
class LogEvent(Event):
...
You can query directly using the Event model like;
events = Event.objects.filter(user=user).order_by('timestamp')
This would give you Event instances, which won't hold data for child models. You can get the child instance from a parent just like following a OneToOneField (thats how django hadnles model inheritance actually, creating two tables and linking them with a foreign key):
event.webhooksubscription
event.webhookevent
event.tokenrefresh
event.logevent
These would return child model instances if exists, and raise an ObjectNotFound exception if the parent does not have a related specified child model instance.
This approach would let you do operations like sorting and slicing at the database level, with the downside of added model complexity (You'll need a separate query to get child model data)
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.
Our current Django project has a model that already has a huge amount of instances in the database. When we started the project we had only one application in mind. Due to new requirements and planning for upcoming applications, we have realized that it might be very beneficial to restructure the database using model inheritance now before it's too late. This is because we can split this model (that has a lot of data in the database) into a base model and another one inheriting from it. The benefit of doing so is that the new applications of the project will also be able to inherit this base model as it contains common fields.
I know that this change would imply a table for the base model and other tables that are related to that table. I am wondering if there's a way that I can perform these changes while minimizing the impact of the data. Ideally, I would like to keep the id's intact, since customers are using the data already.
Since basically all the database is of ModelA, my idea was to convert all the data of this model into a model inheriting from a base model: ModelA(BaseModel) and then get the common fields that I want to be in the BaseModel out of ModelA. Then the subsequent models would all inherit from BaseModel.
I'm just not sure if this can be done of if I would have to make a command, for example, to get all the fields of the existing data and create new data for the new tables.
class BaseModel(models.Model):
id = models.UUIDField(...)
field1 = models.ManyToManyField(...)
field2 = models.TextField(...)
field3 = models.TextField(...)
class ModelA(BaseModel):
id = models.UUIDField(...)
field4 = models.ManyToManyField(...)
field5 = models.TextField(...)
field6 = models.TextField(...)
If you're going to restructure your database, you should export you data just to be safe.
When you eventually make the proposed changes, django will more likely interpret it as drop table and create table.
I have model with custom manager (replaced from properties) that annotate queryset by pseudo fields (adds some annotation inside get_queryset). And its cool because I have one database query per list and so on.
But in some cases this is a problem. For example I want to get my_annotated_field inside signal function or with Django Rest Framework on save ViewSet (where we didn't get object via my manager). Model class doesn't have those fields.
Is there way to solve this problem without extra query like obj = MyModel.objects.get(pk=obj.id)?
I'm programming an online game with a JavaScript client and I use Django REST framework for the backend. I have written a quest system for it.
My quests objects are dynamically created from a django model QuestTemplate which stores information like the Quest desription and the titel (the part that is the same for every user); and another model QuestHistory where I put the information about the state of quest for a certain user: so it has fields like user and completed. They also have some nested objects: Tasks and, Rewards which are created in a similar way to the the Quest objects.
I added a pure python class Quest that combines all the fields of those models, and then I wrote a Serializer for this class. The drawback is that I have to define all the fields again in the QuestSerializer
I have seen that for the ModelSerializer you can use a inner class Meta where you specifiy the model and . Is there also a way to do this with a normal python class instead of a model (with my Quest class).
http://www.django-rest-framework.org/api-guide/serializers#specifying-nested-serialization
Or:
Is it possible to specify more than one model in this inner class, so that it takes fields from my model QuestTemplate and some other fields from my model QuestHistory?
(I'm also not sure about whether this structure makes sense and asked about it here: django models and OOP design )
In the class Meta of the ModelSerializer you can specify only one Model as far as I know. However there are possibilities to add custom fields to the serializer. In your case you could maybe try with:
custom_field = serializers.SerializerMethodField('some_method_in_your_serializer')
You should add the method to your serializer like this:
def some_method_in_your_serializer(self, obj):
# here comes your logic to get fields from other models, probably some query
return some_value # this is the value that comes into your custom_field
And add the custom_field to fields in the class Meta:
class Meta:
fields = ('custom_field', 'all_other_fields_you_need')
Take a look in the documentation about SerializerMethodField for deeper understanding.
I have two models and want to set a relation to them.
class ModelA(models.Model):
id = models.IntegerField(primary_key=True) # DB => PK, AI, NN
name = models.CharField(max_length=50)
...
class ModelB(models.Model):
modelA = models.OneToOneField("ModelA", primary_key=True)
description = models.CharField(max_length=255)
...
So I have a relationship between the two models. Is it possible to add a member to ModelA which stores the relation to ModelB without saving this relation to the database?
I would call it a dynamically created relation or something. Any hints oder suggestions how to let both models know each other?
I think it would be benefiting if the relation on one model can be done dynamically. Otherwise I'll get some trouble storing the models because one of the IDs won't be stored if I save one of the models.
I want to have the relation on both models so I can easily use the models as inline in django-admin.
regards
The reverse relation in Django is created by default.
To get the ModelA you will use:
ModelA.objects.filter(modelb__pk = 1)
You will find more details here:
https://docs.djangoproject.com/en/dev/topics/db/queries/
Django ORM will save ModelA first, then ModelB, in order to maintain data integrity in the DB.
Django can try saving multiple items in one transaction, and this way, if you cancel it, nothing will be saved, but this is possible in shell or in Python code. Over HTTP you can't maintain a transaction over several queries so far.
If you need to show model A as inline of model B, you need a custom admin interface, not new fields/models. I can't tell how to write custom admin widgets. I did do a similar thing with custom editor views & templates & Javascript. I stored the unsaved models in request.session.