Django model attribute to refer to arbitrary model instance - python

I'm working on a logging app in Django to record when models in other apps are created, changed, or deleted. All I really need to record is the user who did it, a timestamp, a type of action, and the item that was changed. The user, timestamp, and action type are all easy, but I'm not sure what a good way to store the affected item is beyond storing an id value and a class name so the item can be retrieved later. I imagine that storing the class name will result in a bit of a hacky solution in order to find the actual class, so I'm wondering if there's a better way. Does anyone know of one?

Use generic relations which do just that (use instance id and model class) but are integrated in Django and you also get a shortcut attribute that returns related instance so you don't have to query it yourself. Example usage.

Check out generic relations.

Related

How to use the data about objects modifications in Django?

Django stores a history of the modification for every object, it is something we can access to through the Django admin:
It contains data about when the object was created/modified, the user who performed the action and the timestamp of the action:
By giving a look at the database, I can guess this data is stored in a default table called django_admin_log:
I am wondering if we can make use of this data in any way through the instance of a model ? I got used to adding manually my timestamps on every models through an Abstract Base Class, but I am wondering if it is useful in any way ?
Or this table records only the modification taking place in the Django admin panel, which would makes the custom timestamp still needed for when the models instance were to be updated outside it.
The history is only related to actions done in the admin view. To add metadata you can also use model_utils, which also offers some other handy functionalities: https://django-model-utils.readthedocs.io/en/latest/
Let us assume every action would be stored in a history table. This would indicate that you always have make a join in the db to get a view where each row also has created and updated information. This is quite some overhead. Therefore, keep it simple and add the timestamp to each model :)

Django ORM : model with a list of items from another model

In my Django models, I have two models : one called Site and the other SiteFeature.
Object-wise, it is very clear how this should work : every instance of the Site class should have as property a list containing instances of the SiteFeature class, simply because the SiteFeature objects should only exist in relation to a Site object.
Database-wise, it is also very clear how it should work : the SiteFeature table should contain a not-nullable column referencing the primary key id column of the Site table, with a foreign key.
But in terms of Django ORM, I don't know how to code this.
Based on this question, and this other example, it seems the classical way to proceed works the other way round :
The Site model class contains no ORM model field referencing the SiteFeature list.
Instead, the SiteFeature ORM model class has a ForeignKey field referencing the Site class.
I see there is a way to code this out : by adding a function to the Site model class that searches all the related SiteFeature, and make this function a property (decorator #property):
#property
def site_features(self):
return SiteFeature.objects.filter(site_id=site_id)
But that leaves me doubts :
The proper logic for me would also be that when I save, update or create an instance of the Site class, it would also automatically save / update / create the instances of SiteFeature that are related to it. (same thing for deleting the object, but that can be covered by the on_delete=models.CASCADE parameter of the ForeignKey field).
I could add my own save_with_features / update_with_features / create_with_features methods that cascade all but I am not sure what would happen in case of calls made automatically by Django to the standard save / update / create such as in bulk operations.
This problem seems to basic that I suppose there is already a proper way to do it. How would that be ?
Eventually, I solved the problem with the sitefeature_set Manager.
Reference: https://docs.djangoproject.com/en/3.0/topics/db/queries/#following-relationships-backward

How to customize Django inline admin forms based on the object instance

The Django documentation makes it clear how to customize ModelForm instances based on the attributes of the particular Model instance being edited. However, I am working with a design that involves a lot of foreign key relationships between models, and I keep running into situations where I would like to modify a particular inline form instance based on the inline Model associated with it. I have dug through the documentation and the code, but the solution for this is eluding me.
The closest thing to a hook for this that I've been able to find is InlineModelAdmin.get_formset(), but the object instance that gets passed to that method is the parent object, not an instance of the child object. My instinct is that there is a way to do this, though. Does anybody know the proper way?
I am not 100% sure I fully understand what you are asking, but you can specify a forms.ModelForm for the admin inline (https://docs.djangoproject.com/en/1.7/ref/contrib/admin/#django.contrib.admin.InlineModelAdmin.form) and that receives an instance of the current inline object and then you can change the form fields based on the instance.

Django data modeling: Foreignkey or hard coded?

Suppose I have a table of animals which has two attributes name and type, whereas type can be: 'dog', 'cat', etc. Here are two ways to implement this in Django: one where type is a ForeignKey to AnimalType:
class Animal(models.Model):
name = models.CharField(max_length=10)
type = models.ForeignKey(AnimalType)
The other is to just have type as a predefined choice which would be defined in the imported module:
class Animal(models.Model):
name = models.CharField(max_length=10)
type = models.CharField(
max_length=10,
choices=ANIMAL_TYPE_CHOICES
)
The latter (predefined choice) seems more efficient to me, since types will never be dynamically updated by user interaction and if a new type needs to be added it will be added by a developer, i.e. the code would be updated rather than the database.
However, I would like to know if this would be a good/acceptable practice? Or should I waste a separate database table for such a "static" entry and also pay with extra time caused by db accesses?
Thanks.
The first way has the advantage that you don't have to touch the code in order to add a new type of animal.
And of course, someone using your app neither.
Adding a new animal type is something trivial and, for instance, you shouldn´t be messing with a working code deployed on a production server for just add an animal type.
If you´re having problems due to your database is empty at start using the application and because of that you don't have any animal types, well, try Django fixtures: Providing initial data for models
I prefer second way.
If you don't need to edit types from admin panel and always will change it with changes in your code, you do not need to have ForeignKeys and separate table.
In case of ForeignKey, you will have additional integrity check on the database level.
It can be useful if you delete some type and do not want to leave it in DB, for example.
I prefer field choices due to performance reasons. Even if the potential choices increases, as long as the functionality is just a choice selection, there's no reason to create an extra table

Making a multi-table inheritance design generic in Django

First of all, some links to pages I've used for reference: A SO question, and the Django docs on generic relations and multi-table inheritance.
So far, I have a multi-table inheritance design set up. Objects (e.g: Car, Dog, Computer) can inherit an Item class. I need to be able to retrieve Items from the DB, get the subclass, and do stuff with it. My design doesn't allow for retrieving the different kinds of objects one by one, so I need to use the Item container to wrap them all into one. Once I have the Item, the Django docs say I can get the subclass by referencing the attribute with the name of the model (e.g: myitem.car or myitem.computer).
I don't know which type of object my item is referencing, so how can I get the child? Is there a built in way to do this? Here are some other ideas that I had: (some crazier than others)
I was thinking I could add some sort
of GenericForeignKey to Item that
references the child, but I doubt it
is even legal for a parent class to
relate via a ForeignKey to a child
class.
I suppose I could have a
ForeignKey(ContentType) in the Item
class, and find the attribute of
Item to get the child based on the
ContentType's name.
Finally, although an ugly method, I might be able to keep a list of object types, and try each as an attribute until a DoesNotExist error is not thrown.
As you can see, these proposed solutions are not that elegant, but I'm hoping I won't have to use one of them and someone here might have a better suggestion.
Thanks in advance
I have done something similar to method 2 in one of my projects:
from django.db import models
from django.contrib.contenttypes.models import ContentType
class BaseModel(models.Model):
type = models.ForeignKey(ContentType,editable=False)
# other base fields here
def save(self,force_insert=False,force_update=False):
if self.type_id is None:
self.type = ContentType.objects.get_for_model(self.__class__)
super(BaseModel,self).save(force_insert,force_update)
def get_instance(self):
return self.type.get_object_for_this_type(id=self.id)
It would be better to compose the models of an Item model and an ItemType model. Subclassing models sounds nice and is useful in a few edge cases, but generally, it is safest and most efficient to stick to tactics that work with your database, rather than against it.

Categories