How to migrate a Django model into models with table inheritance - python

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.

Related

Django Like mechanism. Database performance question

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.

Get multiple models in single QuerySet ordered by timestamp

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)

Is it possible to use a table in the database when it is NOT a Django model?

Is it possible get a queryset from a table in the app database that is NOT a model in the app?
If I have a table that is not a model named "cartable", conceptually, I want to do this:
myqueryset = cartable.objects.all()
Is there a relatively easy way to do this? Thanks!
If you want to access an existing table in your database that is not managed by your application, you can still create a class for it, and tell django to ignore it for migrations.
Just create a model and add the fields you need to access and then add a meta class to tell django to leave it alone.
class MyModel(model.Model):
class Meta:
managed = False
you can read about that at https://docs.djangoproject.com/en/1.9/ref/models/options/#managed
To do so you would need to create a class (not a model), with methods that use raw SQL. You should see more details here on how to do so: https://docs.djangoproject.com/en/1.9/topics/db/sql/#executing-custom-sql-directly
Please note that you will have to manually create the object with the right properties afterwards.
If you wanted to use Django ORM without the models, I don't think it is possible. You could however create a model that matches your db in a separate app and never create migrations for it to ensure you don't accidentally modify the DB.
Short answer is, "not really". Django QuerySet deals with model instances, so everything in QuerySet API is tied into models. Everything expects to return model instances, uses model fields etc.
That said, you should be able to create a model for an existing table. You will need to add db_table to the Meta, so Django knows where the table lives. If you have some indexing, you will need to make sure Django's idea of indexes is the same as the one in the database. indexed=True on fields and unique_together in Meta should help a lot with that.

What *exactly* is the relationship with the models when using ForeignKey or ManyToMany

There is something that is tripping me up with models, and I guess SQL tables in general.
Let us suppose you have these models:
class Manufacturer(models.Model):
name = models.CharField()
company_created = models.CharField()
class Car(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
When you create an instance of Car like say, the following
civic = Car(manufacturer='honda')
A couple questions:
When you create an instance of Car, are you also creating an instance of Manufacturer as a by-product? Or does 'honda' need to exist as an instance already. If not, is there a way to make an instance of both, in say, one form.
Can I make calls on 'civic' for things pertaining to the manufacture? For example, could I get the 'company_created' data from the civic instance? If not, why bother having the relationship in the first place?
Thanks so much in advance. I would really appreciate a thorough answer so I can understand models and relationships fully. And yes, I have read the docs.
Firstly, the thing to remember is that these classes are representations of underlying database tables. A ForeignKey field in a Django model represents a one-to-many relationship in the database, with an _id field representing the ID of another table. Those tables are themselves independent, but are linked via the FK field (and the relevant index constraint, if the database supports them).
That said, in your Car model manufacturer is a required field (because you haven't defined it as null=True). So when you create a Car, you must point it at an already existing Manufacturer - and that manufacturer must have been saved already, so that Django can populate the underlying manufacturer_id field with the ID of the related object
Because Django is aware of the foreign key relationship between the two objects, you can use them when querying. In SQL this would be done via JOINs: Django gives you a special syntax to do queries across those joins, via double underscores. So, for example, if you wanted to get all the cars made by a manufacturer created in 1990 (assuming that's what you mean by the company_created field), you would do this:
Car.objects.filter(manufacturer__company_created='1990')
Django translates this into something like":
SELECT * from car JOIN manufacturer ON car.manufacturer_id=manufacturer.id WHERE manufacturer.company_created='1990';
If you already have your "civic" instance and just want to get access to the related data, this is pure Python object access: civic.manufacturer is the related Manufacturer object, so you can simply do civic.manufacturer.company_created to get the relevant data. Again, Django translates that into the database access, but from your point of view this is simple object composition.
Note that really all this is fully explained in the tutorial, with relationships between Poll and Choice which exactly match your Manufacturer and Car models.
Yes manufacturer need to exist as an instance already.
you can create car instance like this:
manuf= Manufacturer(name='honda',company_created='Benz')
manuf.save()
civic = Car(manufacturer=manuf)
you can get the company_created data from the civic instance by:
civic.manufacturer.company_created

python django one-to-one relations between two models

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.

Categories

Resources