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

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

Related

Having trouble wrapping my head around follower/target models in Models.py

I have just started with making a similar site to Pinterest and the site has follower/target system that I have barely any understanding of. So far, my models.py code is below:
from django.db import models
class User(models.Model):
username = models.CharField(max_length=45, null=True)
email = models.CharField(max_length=200, null=True)
password = models.CharField(max_length=200)
nickname = models.CharField(max_length=45, null=True)
target = models.ManyToManyField(self, through='Follow')
follower = models.ManyToManyField(self, through='Follow')
class Meta:
db_table = 'users'
class Follow(models.Model):
follower = models.ForeignKey(User, on_delete=models.CASCADE, related_name='targets')
target = models.ForeignKey(User, on_delete=models.CASCADE, related_name='followers')
class Meta:
db_table = 'follows'
This code was made with reference to another StackOverflow thread
Django models: database design for user and follower
However, I am having trouble understanding how using "related_name='targets' in 'follower' and "related_name='followers'" in 'target' where I can't see any 'targets'(plural) or 'followers'(plural) in other areas of models.py
Should I get rid of that related_name, since there is no such table called "followers" or "targets"? And if you spot major errors in my code or logic, can you tell me? Thanks!
Should I get rid of that related_name, since there is no such table called followers or targets.
There is never a table named followers or targets. The related_name [Django-doc] is a conceptual relation Django makes to the other model (in this case User). It means that for a User object myuser, you can access the Follow objects that refer to that user through target for example with myuser.followers.all(), so:
Follow.objects.filter(target=myuser)
is equivalent to:
myuser.followers.all()
The default of a related_name is modelname_set, so here that would be follow_set. But if you remove both related_names, then that would result in a name conflict, since one can not add two relations follow_set to the User model (and each having a different semantical value).
if you spot major errors in my code or logic, can you tell me?
The problem is that since ManyToManyFields refer to 'self' (it should be 'self' as string literal), it is ambigous what the "source" and what the target will be, furthermore Django will assume that the relation is symmetrical [Django-doc], which is not the case. You should specify what the source and target foreign keys are, you can do that with the through_fields=… parameter [Django-doc]. It furthermore is better to simply define the related_name of the ManyToManyField in reverse, to avoid duplicated logic.
from django.db import models
class User(models.Model):
username = models.CharField(max_length=45, unique=True)
email = models.CharField(max_length=200)
password = models.CharField(max_length=200)
nickname = models.CharField(max_length=45)
follows = models.ManyToManyField(
'self',
through='Follow',
symmetrical=False,
related_name='followed_by',
through_fields=('follower', 'target')
)
class Meta:
db_table = 'users'
class Follow(models.Model):
follower = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='targets'
)
target = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='followers'
)
class Meta:
db_table = 'follows'
Here a User object myuser can thus access myuser.follows.all() to access all the users that they follow, myuser.followed_by.all() is the set of Users that follow myuser. myuser.targets.all() is the set of Follow objects that he is following, and myuser.followers.all() is the set of Follow objects that are following that user.

Reason for IntegrityError on Field deletion?

I have a webservice setup with Django backend and I am trying to delete entries for my Field objects. Each Field is assigned to a User and there are a number of Assessments done in the Field. The Assessments are linked to the Fields via a foreign key. Upon deletion of the Field object I want to also delete all the Assessments for that Field, but keep the User.
I played around with the on_deletion parameter and if I set it to CASCADE the Django admin page shows me all the associated Assessment objects if I try to delete the Field object. However, I am still getting the following error:
IntegrityError at /admin/dfto/field/ update or delete on table "field"
violates foreign key constraint "assessment_field_uuid_fkey" on table
"assessment" DETAIL: Key
(uuid)=(f3a52c10-33be-42f9-995d-482025cea17b) is still referenced from
table "assessment".
These are my models for Reference:
class Assessment(models.Model):
uuid = models.TextField(primary_key=True)
longitude = models.FloatField(blank=True, null=True)
latitude = models.FloatField(blank=True, null=True)
field_uuid = models.ForeignKey('Field', models.CASCADE, db_column='field_uuid',blank=True, null=True, related_name='assessments')
class Meta:
db_table = 'assessment'
class Field(models.Model):
uuid = models.TextField(primary_key=True)
name = models.CharField(max_length=50, blank=True, null=True)
country = models.CharField(max_length=50, blank=True, null=True)
user_email = models.ForeignKey('User', models.DO_NOTHING, db_column='user_email')
crop_uuid = models.ForeignKey(Crop, models.CASCADE, db_column='crop_uuid')
class Meta:
db_table = 'field'
Can someone explain to me why I am getting this error and/or provide a fix for me?
I think it's because field_uuid have argument 'models.CASCADE' so when you are deleting it, django also tries to delete any refences to this particular key, don't know how to fix it tho.

Return just changed fields in django models

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)

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)

Categories

Resources