Return just changed fields in django models - python

On my app I am using django-reversion and django-reversion-compare apps extensions to controll object version.
When I update object outside admin I would like set_comment() with just updated fields. How can I access to list of updated fields and set they as comment of that reversion?
I understand when I compare object version I see which fields was changed, but I want have preview in table history of changes.
I was trying do this by django-dirtyfields, but it was return all fields.
Add objects:
with reversion.create_revision():
# create or update if exists
p = Product(reference='010101', name='new name')
p.save()
Model:
class Product(models.Model):
reference = models.CharField(max_length=8, unique=True, primary_key=True)
name = models.CharField(max_length=60, null=True)

Related

How to access data using reverse foreign key reference in django

I have a model named UserProfile and a model PersonalInformation. I would like to fetch all the data of PersonalInformation using UserProfile model when the user is logged into the webiste but i have a foreign key refernce in the PersonalInformation model with the UserProfile model so how do i fetch the personal information using UserProfile model?
User Profile Model :
class UserProfile(models.Model):
"""Represents a user's model inside our system"""
email = models.EmailField(max_length=255, unique=True)
name = models.CharField(max_length=255)
profile_picture = models.ImageField(upload_to='photos/%y/%m/%d/')
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
highest_degree_earned = models.CharField(max_length=255, blank=False)
college_name = models.CharField(max_length=255, blank=False)
graduation_year = models.IntegerField(default=2020, blank=False)
Personal Information Model :
class PersonalInformation(models.Model):
"""Represents a user's personal Infromation inside our system"""
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
mobile = models.CharField(max_length=10 ,blank=True)
bio = models.TextField(max_length=200, blank=True)
college_university = models.CharField(max_length=100, blank=False)
course = models.CharField(max_length=100, blank=False)
First of all, in the code, you are showing you have the names of the models wrong. The UserProfile model name is set as PersonalInformation, change it or the migrations won't work (it's not accepted on the database no matter which one you're using).
Referent to the question you're asking, to fetch the related instance of PersonalInformation of a certain UserProfile instance you should just query the next:
user = UserProfile.objects.get(id='') #Introduce the id of the user you want to fetch its personal information.
user.personalinformation_set.all() # This will return you a QuerySet with all the related instances of PersonalInformation class.
user.personalinformation_set.get(id='') #To get a specific one or you may use a filter to get a filtered QS.
If you want, you can use the related_name attribute for ForeignKey class in order to set a different name from personalinformation_set.
I recommend you too to read the Django documentation, it's really well explained and clear I think:
https://docs.djangoproject.com/en/2.2/topics/db/examples/many_to_one/
As I've seen in a comment, you may also think to use a OneToOne relation instead of ForeignKey if you only expect one instance of PersonalInformation per User. The documentation is at:
https://docs.djangoproject.com/en/2.2/topics/db/examples/one_to_one/

customize django admin object detail page url(object change url)

I am trying to override the default django admin change url.
In my table i have composite primary key.
class ABC(models.Model):
code = models.ForeignKey('PQR', models.DO_NOTHING, db_column='code', primary_key=True)
language = models.ForeignKey(Languages, models.DO_NOTHING, db_column='language')
me_name = models.TextField()
common_name = models.TextField(blank=True, null=True)
abbreviation = models.TextField(blank=True, null=True)
class Meta:
managed = False
db_table = 'abc'
unique_together = (('code', 'language'), ('language', 'me_name'),)
Now my admin url in django admin for each object is /admin/home/abc/{{code}}/change/.
But i have repeated codes in objects because primary key is composite of ('code', 'language'). So for objects which have repeated code are throwing
Error
MultipleObjectsReturned at /admin/home/abc/X00124/change/
get() returned more than one ABC -- it returned 2!
here X00124 this code associated with more than one object.
What i want here that override the modeladmins get_urls() method and construct the url /admin/home/abc/{{code}}/{{language}}/change/.
I tried but no success. Help will be appreciated.
I made a virtual key, that represents compound key as a single value, that allows to use Django admin without code changes -
https://viewflow.medium.com/the-django-compositeforeignkey-field-get-access-to-a-legacy-database-without-altering-db-tables-74abc9868026

Creating many to many relation with AUTH_USER_MODEL in django via intermediary model

I am trying to create the following models. There is a ManyToMany relation from Entry to AUTH_USER_MODEL via the EntryLike intermediate model.
class BaseType(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
creation_time = models.DateTimeField(auto_now_add=True)
last_update_time = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Title(BaseType):
text = models.CharField(max_length=100)
description = models.TextField()
class EntryLike(BaseType):
entry = models.ForeignKey(Entry)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
class Entry(BaseType):
title = models.ForeignKey(Title, on_delete=models.PROTECT)
text = models.TextField()
user = models.ForeignKey(settings.AUTH_USER_MODEL)
liked_by_users = models.ManyToManyField(settings.AUTH_USER_MODEL, through='EntryLike', through_fields=('entry', 'user'))
Running migrations on the above model scheme throws the error: AttributeError:'str' object has no attribute 'meta'.
Any help in resolving this error would be highly appreciated. Am new to Django & Python, but not to Web Development.
The issue is that settings.AUTH_USER_MODEL is almost certainly not a model instance. It's probably a string that constrains the choices another model can make - settings would be a strange place to leave a model definition.
To do a MTM between the user model and your field above you need need to do:
from django.contrib.auth.models import User
class Entry(BaseType):
title = models.ForeignKey(Title, on_delete=models.PROTECT)
text = models.TextField()
user = models.ForeignKey(User)
def __str__(self):
return self.title
I've added the str function so that it gives a more sensible return when you're manipulating it in admin/shell.
I'd also question whether you need the second set of fields (removed here), as you can use select related between the Entry and EntryLike join table, without any duplication of the fields - you can probably go that way, it's just a bit unnecessary.
Lastly, I'd note that the way I'm using it above just uses the default User object that comes with Django - you may wish to customise it. or extend the base class as you've done here with your own models' base class.
(All of this is predicated on AUTH_USER_MODEL not being a model instance - if it is, can you post the model definition from settings.py? )

How do I reference different model types from one field in Django?

I have a model representing a Log Entry. This is created anytime there is a modification to the DB.
I would like to include a foreign key field which refers to the model object that was changed in the Log Entry.
Is such a thing possible?
For example:
Log Entry 1
---> Modified Object Field = User Object
But now instead of User being modified, Blog was modified...
Log Entry 2
---> Modified Object Field = Blog Object
Take a look at GenericForeignKey:
A normal ForeignKey can only “point to” one other model [...] The contenttypes application
provides a special field type (GenericForeignKey) which works around
this and allows the relationship to be with any model.
This is possible using generic relations and the GenericForeignKey
https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#generic-relations
Keep in mind it becomes more involved to filter across the generic foreign key ( you need to get the foreignkey content types first)
You can use as Nigel Tufnel says a GenericForeignKey but I think you are looking for something like the Django's admin log, if you take around the Django's code you can see that it uses a ForeignKey to ContentType and a message:
class LogEntry(models.Model):
action_time = models.DateTimeField(_('action time'), auto_now=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
content_type = models.ForeignKey(ContentType, blank=True, null=True)
object_id = models.TextField(_('object id'), blank=True, null=True)
object_repr = models.CharField(_('object repr'), max_length=200)
action_flag = models.PositiveSmallIntegerField(_('action flag'))
change_message = models.TextField(_('change message'), blank=True)

how to display many error django

i have following models
class tags(models.Model):
tag = models.CharField(max_length=15) # Tag name
tagDescription = models.TextField() # Tag Description
tagSlug = models.TextField() # Extra info can be added to the existing tag using this field
class stores(models.Model):
storeName = models.CharField(max_length=15) # Store Name
storeDescription = models.TextField() # Store Description
storeURL = models.URLField() # Store URL
storePopularityNumber = models.IntegerField(max_length=1) # Store Popularity Number
storeImage = models.ImageField(upload_to=storeImageDir) # Store Image
storeSlug = models.TextField() # This is the text you see in the URL
createdAt = models.DateTimeField() # Time at which store is created
updatedAt = models.DateTimeField() # Time at which store is updated
storeTags = models.ManyToManyField(tags)
class tagsAdmin(admin.ModelAdmin):
list_display = ('tag', 'tagDescription', 'tagSlug')
class storesAdmin(admin.ModelAdmin):
list_display = ('storeName','storeDescription','storeURL',
'storePopularityNumber','storeImage',
'storeSlug','createdAt','createdAt','storeTags'
)
admin.site.register(tags,tagsAdmin)
admin.site.register(stores,storesAdmin)
Whenever I am trying to run command : python manage.py syncdb
I got the error: django.core.exceptions.ImproperlyConfigured: 'storesAdmin.list_display[8]', 'storeTags' is a ManyToManyField which is not supported.
I don't understand what I am doing wrong here. I want to simply display all the models in the django admin site.
You can't reference a Many2ManyField like that, you have to use a method instead in the stores class that looks like this
def get_tags():
return self.storeTags.all()
and reference that in your list_display(...'get_tags')
This is done because the M2M field would result in lots of SQL queries that would slow the entire thing down so therefore the choice would have to come from the developer and not from the framework.
Please check:
ModelAdmin.list_display
"ManyToManyField fields aren’t supported, because that would entail executing a separate SQL statement for each row in the table. If you want to do this nonetheless, give your model a custom method, and add that method’s name to list_display. (See below for more on custom methods in list_display.)"
You can use a custom method to show values of ManyToManyField or simply remove storeTags from list_display

Categories

Resources