Django , how to show 'secondary property' of parent in TabularInline - python

I try to do tabular inline admin.
In the child tab, if we include a ForeignKey field, it will show the str property of that foreign model.
But how to also show another property of that foreign model?
Here is my models.py
class RawMaterial(models.Model):
name = models.CharField(max_length=15)
ubuy = models.CharField(max_length=5)
usell = models.CharField(max_length=5)
uconv = models.DecimalField(max_digits = 5,decimal_places=2)
def __str__(self):
return self.name
class Coctail(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Ingredient(models.Model):
coctail = models.ForeignKey(Coctail,
related_name='ingredient',
on_delete=models.CASCADE)
rawmaterial = models.ForeignKey(RawMaterial,
related_name='ingredient',
on_delete=models.CASCADE)
qty = models.DecimalField(max_digits = 5,decimal_places=2)
def __str__(self):
return self.rawmaterial
def rawusell(self):
return self.rawmaterial.usell
rawusell.short_description = 'UOM'
Here is my admin.py
from django.contrib import admin
# Register your models here.
from .models import *
admin.site.register(RawMaterial)
class IngredientInline(admin.TabularInline):
model = Ingredient
list_display = ('rawmaterial', 'qty', 'rawusell')
class CoctailAdmin(admin.ModelAdmin):
inlines = [IngredientInline]
admin.site.register(Coctail, CoctailAdmin)
and here what I got
My question is : How to show rawmaterial.usell in Ingredient tab?
Sincerely
-bino-

You can show the rawmaterial.usell field in the ingredient tab but it will not be editable. Since, any field can only be editable if they are a field of that model (Without using any custom form and logic).
So, if you want rawmaterial.usell to be editable, you will have to make a rawmaterial admin
You can show the rawmaterial.usell in IngredientInline by doing this.
class IngredientInline(admin.TabularInline):
model = Ingredient
readonly_fields = ('rawusell', )
list_display = ('rawmaterial', 'qty', 'rawusell')
def rawusell(self, obj):
return obj.rawmaterial.usell
This will start showing usell in the inline admin.

Related

Writing a view to allow user to add related data for a customer, like comments for example; on a single page

I have working models, forms, views and urls for a django CRUD app managing customer functions for a business. I just cant seem to figure out how to write a view to allow a user to add comments, or other data related to the customer and stored in other models using a single view and template.
So for example; for customer a, all the comments for customer a with the option to add, amend etc.. and the same for the other related models.
I understand how to do it for one I will be able to make quick progress. (old school programmer here)
Here is what I am working with - keeping it simple.
MODELS
class Emergency(models.Model):
# Fields
name = CharField(null = False, blank = False, max_length=60)
address = TextField(blank=True, null=True, help_text='Street and town', verbose_name='Address')
telephone = CharField(blank=False, null=False, unique= True, max_length=20)
relationship = CharField(choices=(('P', 'Parent'),('S', 'Son'),('D', 'Daughter'),('R', 'Relative'),('L', 'Partner')),max_length = 1,default='R')
class Meta:
ordering = ('-pk',)
def __unicode__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse('conform_emergency_detail', args=(self.pk,))
def get_update_url(self):
return reverse('conform_emergency_update', args=(self.pk,))
class Client(models.Model):
# Fields
surname = CharField(null = False, blank = False, max_length=30)
name = CharField(null = False, blank = False, max_length=60)
# Relationship Fields
emergencycontact = models.ForeignKey(Emergency, on_delete=models.CASCADE, name = 'Emergency Contact')
class Meta:
ordering = ('-pk',)
def __unicode__(self):
return u'%s' % self.pk
def get_absolute_url(self):
return reverse('conform_client_detail', args=(self.pk,))
def get_update_url(self):
return reverse('conform_client_update', args=(self.pk,))
class Clientnotes(models.Model):
# Fields
slug = AutoSlugField(populate_from='name', blank=True)
created = DateTimeField(auto_now_add=True, editable=False)
last_updated = DateTimeField(auto_now=True, editable=False)
note = CharField(blank=False, null=False, max_length= 300 )
# Relationship Fields
modified_by = models.ForeignKey(User, related_name='clientnotes_modified_by', on_delete=models.CASCADE, name= 'Changed by')
clientnotes = models.ManyToManyField(Client, name = 'Clients notes')
class Meta:
ordering = ('-created',)
def __unicode__(self):
return u'%s' % self.slug
def get_absolute_url(self):
return reverse('conform_clientnotes_detail', args=(self.slug,))
def get_update_url(self):
return reverse('conform_clientnotes_update', args=(self.slug,))
FORMS
class ClientForm(forms.ModelForm):
class Meta:
model = Client
fields = ['surname', 'name']
class ClientnotesForm(forms.ModelForm):
class Meta:
model = Clientnotes
readonly_fields = ['slug', 'modified_by']
fields = ['note']
VIEWS
class ClientListView(ListView):
model = Client
class ClientCreateView(CreateView):
model = Client
form_class = ClientForm
class ClientDetailView(DetailView):
model = Client
class ClientUpdateView(UpdateView):
model = Client
form_class = ClientForm
TEMPLATE NAMES
client_detail.html
client_form.html
client_list.html
I have simple views, forms and templates to list, view detail and add and it all works well - with the exception of related models because i am not able to add both models at the same time. I need a simple clear simpletons guide with what i have provided so it clicks into place.

Django model reference and manipulation

I have the following models in Django that have a structure as follows:
class Office_Accounts(models.Model):
accountid = models.EmailField(max_length=200, unique=True)
validtill = models.DateField(default=datetime.now)
limit = models.CharField(max_length=2)
class Device(models.Model):
device_type = models.ForeignKey(DeviceType,to_field='device_type')
serial_number = models.CharField(max_length=200,unique=True)
in_use_by = models.ForeignKey(User,to_field='username')
brand = models.CharField(max_length=200,default="-", null=False)
model = models.CharField(max_length=200,default="-", null=False)
type_number = models.CharField(max_length=200,blank=True,null=True, default = None)
mac_address = models.CharField(max_length=200,blank=True,null=True, default = None)
invoice = models.FileField(upload_to='Device_Invoice', null=True, blank = True)
msofficeaccount = models.ForeignKey(Office_Accounts, to_field="accountid")
class Meta:
verbose_name_plural = "Devices"
def full_name(self):
return self.device_type + self.serial_number + self.brand
I will display both of the models in admin.py.
Now, I want to display the count of each accountid present in the field "msofficeaccount" (present in Device Models) in my admin page of Office_Accounts model. For an example if xyz#abc.com appears in 10 rows of msofficeaccount field then, the count should be displayed as 10 in Office_Accounts admin page. Can anyone please guide me how should I approach this problem to solve it?
You could add a method to your admin class that returns the count of related devices for each office_account, but that would be very inefficient. Instead you can override get_queryset to annotate the count from a database aggregation function:
from django.db.models import Count
class Office_AccountsAdmin(admin.ModelAdmin):
list_display = (..., 'device_count')
...
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.annotate(device_count=Count('device'))
(On a minor note, Python style is always to use CamelCase for class names, and Django style is to use singular model names, so your model should really be called OfficeAccount.)

Django Admin panel- Adding a custome field in 'list_display' widget which is not an attribute of models class(without altering models.py)

I want to add a field named 'count' to 'genre' class that gives the count of movies in that genre.
models.py
class genre(models.Model):
genre_name = models.CharField(max_length=20, verbose_name='Genre')
def __str__ (self):
return(self.genre_name)
class movie(models.Model):
movie_name = models.CharField(max_length=50, verbose_name='Movie Name')
movie_rating = models.IntegerField(default=1)
movie_genres = models.ManyToManyField(genre, verbose_name='Genre(s)')
added_by = models.CharField(max_length=20, blank=True)
email = models.EmailField(blank=True)
Date = models.DateTimeField(default=datetime.now)
def __str__ (self):
return(self.movie_name)
admin.py
from __future__ import unicode_literals
from django.contrib import admin
from . models import movie, genre
from django import forms
class movieAdmin(admin.ModelAdmin):
list_display=('movie_name', 'movie_rating','Date')
search_fields=('movie_name',)
filter_horizontal=('movie_genres',)
list_filter=('Date',)
date_hierarchy='Date'
ordering=('-movie_name',)
fields=('movie_name','movie_rating')
admin.site.register(movie, movieAdmin)
admin.site.register(genre)
I want to add count field in list_display
Write a method (either in the modelAdmin or the model itself) to get the required value. Then add it to the list_display like a model's field (without ())
list_display = ('somefield', .. , 'myCountMethod',)
# in modelAdmin or model
def myCountMethod(self, ...):
# do some counts here
return display_value

Populate Django ManyToManyField Options Based on Other Field in Admin

I am having problems filtering options for a ManyToManyField on the Django Admin Add screen based on input to another field on the same form. I am new to Django and have been unable to use any of the generic fixes described elsewhere because they are all slightly different than my situation. Here is my situation:
I have three models in my project: Class, Student, and AttendanceRecord. In the Django Admin, when adding an attendance record, I would like to change the options for the field Absent_Students based on the selection made for the field Associated_Class. So, for example, if Associated_Class "CS 450" is selected, the options for Absent_Students should change to only students whose class_list includes CS 450.
Here are my models:
from __future__ import unicode_literals
from django.db import models
from django.contrib.auth.models import User
from django.utils.encoding import python_2_unicode_compatible
import random, string
# Create your models here.
#This is the model for a student
#python_2_unicode_compatible
class Student(models.Model):
pass
Student_First_Name = models.CharField(max_length=200)
Student_Last_Name = models.CharField(max_length=200)
Student_ID_Number = models.CharField(max_length=200)
Student_Class = models.ForeignKey('Class', null=True)
def __str__(self):
return self.Student_Last_Name + ',' + self.Student_First_Name
# This is the model for a class
#python_2_unicode_compatible
class Class(models.Model):
class Meta:
verbose_name_plural = "Classes"
Class_Name = models.CharField(max_length=200)
Student_List = models.ManyToManyField('Student', related_name='class_list')
Professor = models.ForeignKey(User,null=True)
AddCode = models.IntegerField
pass
def __str__(self):
return self.Class_Name
def getName(self):
return self.Class_Name
def getProfessor(self):
return self.Professor.id
def getProf(self):
return self.Professor
def getStudents(self):
return self.Student_List
#This is the model for attendance records
class AttendanceRecord(models.Model):
class Meta:
verbose_name = "Attendance Record"
Associated_Class = models.ForeignKey(Class, on_delete=models.CASCADE, related_name='Attendance_Records')
Date = models.DateField()
Absent_Students = models.ManyToManyField('Student', blank=True)
Present_Students = models.ManyToManyField('Student', related_name='a')
def get_associated_class_id(self):
return self.Associated_Class
def __str__(self):
return self.Associated_Class.__str__() + ' on date ' + self.Date.__str__(self)
I have tried doing this by editing the AttendanceRecordAdminForm class and AttendanceRecordAdmin class. My problem is that when setting the self.fields['Absent_Students].queryset I do not know how to access the currently selected Associated_Class on the form. I keep getting an error that "AttendanceRecord has no Associated_Class". Here are those classes just discussed in their entirety:
class AttendanceRecordAdminForm(forms.ModelForm):
class Meta:
model = AttendanceRecord
fields = '__all__'
def __init__(self, *args, **kwargs):
super(AttendanceRecordAdminForm, self).__init__(*args, **kwargs)
instance = kwargs.get('instance', None)
self.fields['Absent_Students'].queryset = Student.objects.filter(class_list__id=self.instance.get_associated_class_id())
self.fields['Present_Students'].queryset = Student.objects.filter(class_list__id=1)
class AttendanceRecordAdmin(admin.ModelAdmin):
form = AttendanceRecordAdminForm
filter_horizontal = ('Absent_Students', 'Present_Students',)
Basically, I am looking for a way to access the currently entered Associated_Class on the admin form so I can properly filter the queryset.
After hours more of online searching I finally found what I needed. A chained ManyToMany from the smart_select app makes this very easy. This link: How to use django-smart-select describes the install process and also links to the documentation for using it once it is installed. Hopefully this helps some others as well.

Django Admin how to change text in relations field

I have the following code:
models.py
from django.db import models
from parler.models import TranslatableModel, TranslatedFields
class Federation(TranslatableModel):
translations = TranslatedFields(
name = models.CharField('name', max_length=50)
)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Athlete(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
federation = models.ForeignKey('Federation', on_delete=models.SET_NULL, null=True)
height = models.IntegerField();
weight = models.IntegerField();
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
admin.py
from django.contrib import admin
from parler.admin import TranslatableAdmin
from .models import Athlete, Federation
class AthleteAdmin(admin.ModelAdmin):
list_display = ['first_name', 'last_name', 'height', 'weight', 'get_federation_name']
fields = ['first_name', 'last_name', 'height', 'weight', 'federation']
def get_federation_name(self, obj):
obj.federation.set_current_language('en')
return obj.federation.name
get_federation_name.short_description = 'Federation'
class FederationAdmin(TranslatableAdmin):
search_fields = ['translations__name']
list_display = ['name']
fields = ['name']
admin.site.register(Federation, FederationAdmin)
admin.site.register(Athlete, AthleteAdmin)
The federation field is shown as a list but the text in the select menu is shown as "Federation object." For the list, I created a function to fetch the data from related Federation model's translation relation. I want to do the same with the form fields. If I get this to work in form fields without a function, I will also change the list display to work the same way.
I am new to Python and Django (first time) and I can't seem to find a solution to this problem.
Thank you!
By default it uses the __str__() method of the object. So the easiest way to change this is to set this method. For instance:
class Federation(models.Model):
...
def __str__(self):
return "{0} ({1})".format(self.translation, self.created_at)
Another way — if you don't want to override the __str__ method — would be to override the label_from_instance of the form Field itself. But it's more tricky.
def _federation_label_from_instance(self, obj):
return "{0} ({1})".format(obj.translation, obj.created_at)
class AthleteAdmin(admin.ModelAdmin):
...
def formfield_for_foreignkey(self, db_field, request, **kwargs):
formfield = super().form_field(db_field, request, **kwargs)
if db_field.name == 'federation':
formfield.label_from_instance = _federation_label_from_instance
return formfield

Categories

Resources