Django Admin list display cannot access foreign key attribute getting 'NoneType' object has no attribute - django-admin

I'm trying to display a related field in the admin section but I'm getting the following error:
'NoneType' object has no attribute 'ref_code'
models.py
class Orden(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
producto = models.ManyToManyField(
ProductosOrden)
medio_de_venta = models.ForeignKey(MediosVenta, null=True, blank=False, on_delete=models.SET_NULL)
fecha_venta = models.DateField(blank=False, null=False)
ordenado = models.BooleanField(default=False)
ref_code = models.CharField(max_length=20, blank=True, null=True)
promo = models.ForeignKey(Promos,null=True, blank=True, on_delete=CASCADE )
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
vendido_por = models.CharField(max_length=20, null=False, blank=False)
class Meta:
verbose_name_plural = "Ordenes"
ordering = ["-id"]
def __str__(self):
return self.user.username
class Cliente(models.Model):
nombre = models.CharField(max_length=60, null=False, blank=False)
email = models.EmailField(blank=False, null=False)
email_valido = models.BooleanField(null=True, blank= True, verbose_name='Email Válido')
telefono = models.CharField(
max_length=17, null=True, blank=True, verbose_name='Teléfono')
lead = models.ForeignKey(Leads, models.SET_NULL, null=True, blank=True)
activo = models.BooleanField(default=False)
orden = models.ForeignKey(Orden, null=True, blank=True, on_delete=CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
created_by = models.CharField(max_length=20, null=False, blank=False)
class Meta:
verbose_name_plural = "Clientes"
ordering = ["-id"]
def __str__(self):
return self.nombre
admin.py
class ClienteAdmin(admin.ModelAdmin):
list_display = ('id', 'nombre', 'activo', 'get_ref_code')
readonly_fields=('id',)
search_fields = ['nombre']
actions = ['activa','desactivar']
#admin.action(description='Desactivar')
def desactivar(self, request, queryset):
queryset.update(activo = False)
#admin.action(description='Activar')
def desactivar(self, request, queryset):
queryset.update(activo = True)
#admin.display(description= 'ref_code')
def get_ref_code(self, obj):
if not obj.orden.ref_code:
return None
else:
return obj.orden.ref_code
If I just use the return as:
return obj.orden
results are displayed with the name, but I want to display the ref_code results which is under the Orden model and it is not working.

correct if condition because orden column is nullable in Cliente model.
#admin.display(description= 'ref_code')
def get_ref_code(self, obj):
if not obj.orden_id:
return None
else:
return obj.orden.ref_code

Related

how to display the fields of related models in the admin panel through relationships model

I'm having trouble with the admin panel. The problem arose due to the presence of relationships model 'ElementVersion'. I don’t know how to display the fields of related models in the admin panel through ElementVersion.
My models.py file
class Refbook(models.Model):
code = models.CharField(max_length=100, unique=True, verbose_name='refbook code')
name = models.CharField(max_length=300, verbose_name='refbook name')
description = models.TextField()
def __str__(self):
return self.name
class Version(models.Model):
refbook_id = models.ForeignKey('Refbook',
on_delete=models.CASCADE,
related_name='versions',
verbose_name='refbook name')
version = models.CharField(max_length=50, verbose_name='refbook version')
date = models.DateField(verbose_name='start date')
def __str__(self):
return self.version
class Meta:
unique_together = [['refbook_id', 'version'], ['refbook_id', 'date']]
class Element(models.Model):
version_id = models.ManyToManyField('Version',
through='ElementVersion',
verbose_name='id_version')
code = models.CharField(max_length=100, unique=True, verbose_name='element's code')
value = models.CharField(max_length=300, verbose_name='element's value')
def __str__(self):
return self.code
class ElementVersion(models.Model):
version = models.ForeignKey(Version,
on_delete=models.CASCADE,
verbose_name='version')
element = models.ForeignKey(Element,
on_delete=models.CASCADE,
verbose_name='element')
class Meta:
unique_together = ('version', 'element')
This is the admin.py file
from .models import Refbook, Version, Element, ElementVersion
class VersionInline(admin.StackedInline):
model = Version
fields = ['date']
readonly_fields = ('date',)
def has_add_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
#admin.register(Refbook)
class RefbookAdmin(admin.ModelAdmin):
list_display = ['id', 'code', 'name', 'get_last_version', 'get_date_version']
inlines = [VersionInline]
def get_last_version(self, obj):
return obj.versions.values_list('version').last()
get_last_version.short_description = "last version"
def get_date_version(self, obj):
return obj.versions.values_list('date').last()
get_date_version.short_description = "start date"
class ElementVersionInline(admin.TabularInline):
model = ElementVersion
extra = 0
#admin.register(Version)
class VersionAdmin(admin.ModelAdmin):
list_display = ['get_code_refbook', 'refbook_id', 'version', 'date']
inlines = [ElementVersionInline]
def get_code_refbook(self, obj):
return obj.refbook_id.code
get_code_refbook.short_description = 'refbook code'
#admin.register(Element)
class ElementAdmin(admin.ModelAdmin):
list_display = ['id', 'code', 'value']
inlines = [ElementVersionInline]
I want to solve two problems:
on the version editing page, you need the ability to fill in the elements in this version. At the same time, I want all fields with readable names to be displayed, that is, the code and value
on the element editing page, you need the ability to select "reference-version" from the double list

I want to show in django admin the data of a field that is in an intermediate many-to-many table

I want to obtain the data of a field that is in an intermediate table of a many-to-many relationship, the field I want is quantity, it is in PecosaMaterial
this is my code:
models.py
class Material(models.Model):
nombre_material = models.CharField(max_length=200, verbose_name="Nombre", null=True)
class Meta:
verbose_name = "Material"
verbose_name_plural = "Materiales"
def __str__(self):
texto_material = "{0} | {1}"
return texto_material.format(self.codigo_material, self.nombre_material, self.precio_unitario)
class Pecosa(models.Model):
matpecosa = models.ManyToManyField(Material, through="PecosaMaterial")
descripcion_pecosa = models.TextField(verbose_name="Justificación")
class Meta:
verbose_name = "pecosa"
verbose_name_plural = "pecosas"
def __str__(self):
return self.descripcion_pecosa
class PecosaMaterial(models.Model):
pecosa = models.ForeignKey(Pecosa, on_delete=models.CASCADE)
material = models.ForeignKey(Material, on_delete=models.CASCADE)
cantidad = models.IntegerField(verbose_name="Cantidad")
precio_total_material = models.DecimalField(max_digits=12, decimal_places=6, verbose_name="Precio total del material", null=True, blank=True)
def __unicode__(self):
return self.pecosa
admin.py:
class PecosaAdmin(ImportExportModelAdmin,admin.ModelAdmin):
inlines = [PecosaMaterialInline,]
list_per_page = 15
exclude = ('precio_total_pecosa',)
resource_class = PecosaAdminResource
# Solucionar el problema de las busquedas
# search_fields = ('solicitante',)
readonly_fields = ('creacion_pecosa', 'modificacion_pecosa')
list_display = ('descripcion_pecosa', 'cantidad', 'materiales', 'precio_unitario', 'precio_total_pecosa', 'solicitante', 'creacion_pecosa')
from .models import PecosaMaterial
def cantidad(self, obj):
cantidad = PecosaMaterial.objects.all()
return cantidad
def precio_unitario(self, obj):
return format_html("</br>".join([str(p.precio_unitario) for p in obj.matpecosa.all()]))
def materiales(self, obj):
return format_html("</br>".join([m.nombre_material for m in obj.matpecosa.all()]))
ordering = ('creacion_pecosa',)
list_filter = ('creacion_pecosa',)
As you can see with the cantidad function, I tried to obtain the data but it did not work, sorry for the bad code and thanks in advance
from .models import PecosaMaterial
def cantidad(self, obj):
cantidad = PecosaMaterial.objects.all()
return cantidad

How to amend Models.py to Changes url representation

I am implementing a project on django rest framework (filemanager). I need the url field for the "FILE" data type to be in this form as "url": "/file/url1". For the "FOLDER" class, the "url" field should output "url": None. What do I need to write for this in model.py .
Current model.py code looks like:
FILE='FILE'
FOLDER='FILE'
TYPE_CHOICES = [(FILE,"FILE"),(FILE,"FOLDER")]
class Folder(models.Model):
id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, null=False)
parentId = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True)
type = models.CharField(max_length=255, choices=TYPE_CHOICES, editable=False, default='FOLDER')
size = models.IntegerField(blank=True, null=True, default = 0)
date = models.DateTimeField(auto_now=True)
url = None
# children = models.ForeignKey('self', models.SET_NULL, null=True)
class File(models.Model):
type = models.CharField(max_length=255, choices=TYPE_CHOICES, editable=False, default='FILE')
id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, null=False)
file = models.FileField(null=True, max_length=255)
date = models.DateTimeField(auto_now =True)
parentId = models.ForeignKey(Folder, on_delete=models.CASCADE, null=True, blank=True, related_name='children')
# url = models.URLField(max_length=200, default=None, null=True, blank=True)
size = models.IntegerField(null=True, blank=True, default=0)
def __str__(self):
return str(self.file.name)

Undefined join condition between parent/child tables

Here's a model for a basic todo app, in which a user can create a tasklist that contains tasks:
from flask.ext.sqlalchemy import SQLAlchemy
from allot import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String)
tasklists = db.relationship('Tasklist', backref='user', lazy='dynamic')
tasks = db.relationship('Task', backref='user', lazy='dynamic')
def __init__(self, username, email):
self.username = username
class Tasklist(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
user_id = db.Column(db.Integer, db.ForeignKey('User.id'))
tasks = db.relationship('Task', backref='tasklist', lazy='dynamic')
def __init__(self, title, description, user_id):
self.title = title
self.user_id = user_id
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title= db.Column(db.String)
user_id = db.Column(db.Integer, db.ForeignKey('User.id'))
tasklist_id = db.Column(db.Integer, db.ForeignKey('Tasklist.id'))
def __init__ (self, title, status, user_id):
self.title = title
self.user_id = user_id
self.tasklist_id = tasklist_id
It generates this error:
ArgumentError: Could not determine join condition between parent/child
tables on relationship User.tasklists. Specify a 'primaryjoin'
expression. If 'secondary' is present, 'secondaryjoin' is needed as
well.
Adding primaryjoin arguments in the relationships, like this:
tasklists = db.relationship('Tasklist', backref='user', primaryjoin='user.id==Tasklist.user_id', lazy='dynamic')
... returns:
InvalidRequestError: One or more mappers failed to initialize - can't
proceed with initialization of other mappers.
What's wrong with the model?
I searched for an explanation in the [relationship][1] doc and on SO but to no avail.
One possibility is that when you're referencing foreign keys your using the uppercase "User" when you actually need to use the lowercase tablename reference. Try this:
from flask.ext.sqlalchemy import SQLAlchemy
from allot import db
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String)
tasklists = db.relationship('Tasklist', backref='user', lazy='dynamic')
tasks = db.relationship('Task', backref='user', lazy='joined'))
def __init__(self, username, email):
self.username = username
class Tasklist(db.Model):
__tablename__ = 'tasklist'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
tasks = db.relationship('Task', backref='tasklist', lazy='dynamic')
def __init__(self, title, description, user_id):
self.title = title
self.user_id = user_id
class Task(db.Model):
__tablename__ = 'task'
id = db.Column(db.Integer, primary_key=True)
title= db.Column(db.String)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
tasklist_id = db.Column(db.Integer, db.ForeignKey('tasklist.id'))
def __init__ (self, title, status, user_id):
self.title = title
self.user_id = user_id
self.tasklist_id = tasklist_id

Order Django admin change list column by output of __unicode()__ method

Here's part of my Django app's models.py:
class Person(models.Model):
birth_year = WideYear(null=True, blank=True)
birth_year_uncertain = models.BooleanField()
death_year = WideYear(null=True, blank=True)
death_year_uncertain = models.BooleanField()
flourit_year = WideYear(null=True, blank=True)
flourit_year_uncertain = models.BooleanField()
FLOURIT_CHOICES = (
(u'D', u'Birth and death dates'),
(u'F', u'Flourit date'),
)
use_flourit = models.CharField('Date(s) to use', max_length=2, choices=FLOURIT_CHOICES)
index_entries = models.ManyToManyField(IndexEntry, null=True, blank=True)
def __unicode__(self):
if self.personname_set.filter(default_name__exact=True):
name = z(self.personname_set.filter(default_name__exact=True)[0])
else:
name = u'[Unnamed person]'
if self.use_flourit == u'D':
dates = '%s - %s' % (z(self.birth_year), z(self.death_year))
else:
dates = 'fl. ' + z(self.flourit_year)
return '%s (%s)' % (name, dates)
class PersonName(models.Model):
titles = models.CharField(max_length=65535, null=True, blank=True)
surname = models.CharField(max_length=255, null=True, blank=True)
first_name = models.CharField(max_length=255, null=True, blank=True)
middle_names = models.CharField(max_length=255, null=True, blank=True)
post_nominals = models.CharField(max_length=65535, null=True, blank=True)
default_name = models.BooleanField()
person = models.ForeignKey(Person, null=True, blank=True)
def __unicode__(self):
return '%s, %s %s' % (self.surname, self.first_name, self.middle_names)
class Meta:
unique_together = ("titles", "surname", "first_name", "middle_names", "post_nominals", "person")
unique_together = ("default_name", "person")
and here are the corresponding parts of my app's admin.py:
from reversion.admin import VersionAdmin
class PersonNameInline(admin.TabularInline):
model = PersonName
extra = 1
class PersonAdmin(VersionAdmin):
radio_fields = {"use_flourit": admin.HORIZONTAL}
inlines = [PersonNameInline]
admin.site.register(Person, PersonAdmin)
In the admin, this produces a change list as follows:
(source: sampablokuper.com)
As you can see, although the change list populates each row of the Person column with the output of the __unicode()__ method of the Person class, it does not order the rows of that column by the __unicode()__ method of the Person class.
How can I make it do so?
Many thanks in advance!
Django ordering is done in the database level. Unless you store the result of your unicode function in the DB, django is not going to be able to natively return results ordered in that fashion.
Storing an ordering value in the DB is probably the most expedient way to solve this problem.
Just hit the same problem, and I found the following link useful. Instead of sorting by unicode, it tries to sort by multiple columns, which may help solve the problem:
http://djangosnippets.org/snippets/2110/
Following Paul McMillan's suggestion, I added the following line to the class definition of Person:
ordering_string = models.CharField(max_length=255, null=True, blank=True)
I also put the this above the PersonName definition in models.py:
def post_save_person_and_person_name(sender, **kwargs):
person_name = kwargs['instance']
person = person_name.person
if person.ordering_string != unicode(person)[0:254]:
person.ordering_string = unicode(person)[0:254]
super(Person, person).save()
and put this below the PersonName definition in models.py:
post_save.connect(post_save_person_and_person_name, sender=PersonName)
So far, so good.
I think I might be able to improve on it by replacing the save() call above with a queryset update() . I'd welcome suggestions on that front!

Resources