Django listings: django displays FK's object instead of value - django

I am using django_listing in order to create a table:
class SpotTableView(ToolbarListingView):
template_name = f'{templates_dir}tables/spot.html'
update_success_redirect_url = LISTING_REDIRECT_NO_EDIT
save_to_database = True
def get_listing_instance_update_listing(self):
return Listing(Spot,
editable=True,
editing=False,
edit_on_demand_options={'has_paginator': False},
edit_on_demand=True,
gender__input_type='radio',
save_to_database=True,
selection_position='left',
selection_multiple=True,
selectable=True,
selection_initial=[3, 4],
per_page=5,
has_toolbar=True,
toolbar_placement='top',
toolbar=Toolbar(
InsertToolbarItem(),
EditToolbarItem(),
DeleteToolbarItem(),
SortSelectToolbarItem(),
PerPageSelectToolbarItem(choices='5,10,25,50,100,-1:All'),
)
)
When the data table loads the FK's object name is displayed instead of the integer representing the FK's model's index.
table
I am declaring the FK inside the model as following: mid = models.ForeignKey('Metadata', default=1, verbose_name="mid", on_delete=models.SET_DEFAULT, db_column="mid")
How can i make it display the value of the FK, instead of the model?

Define __str__ inside the "Metadata" model:
#models.py
class Metadata(models.Model):
....
....
def __str__(self):
return self.id
More info: https://docs.djangoproject.com/en/dev/ref/models/instances/#str

Related

how to call back unique together constraints as a field django

i'm trying to call back unique constraints field , in my project i have to count number of M2M selected
class Booking(models.Model):
room_no = models.ForeignKey(Room,on_delete=models.CASCADE,blank=True,related_name='rooms')
takes_by = models.ManyToManyField(Vistor)
#property
def no_persons(self):
qnt = Booking.objects.filter(takes_by__full_information=self).count()#but this doesnt work
return qnt
Cannot query "some room information": Must be "Vistor" instance.
class Vistor(models.Model):
full_name = models.CharField(max_length=150)
dob = models.DateField(max_length=14)
city = models.ForeignKey(City,on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(fields=['full_name','dob','city'],name='full_information')
]
def __str__(self):
return f'{self.full_name} - {self.city} - {self.dob}'
it it possible to access full_information through Booking model ? thank you ..
If you want to count the number of Visitors related to that booking, you can count these with:
#property
def no_persons(self):
self.taken_by.count()
This will make an extra query to the database, therefore it is often better to let the database count these in the query. You can thus remove the property, and query with:
from django.db.models import Count
Booking.objects.annotate(
no_persons=Count('takes_by')
)
The Bookings that arise from this QuerySet will have an extra attribute no_persons with the number of related Visitors.

How to get the related child type when the parent can have multiple child type?

Let's say I have 3 models like this:
class Parent(models.model):
CHILD_CHOICES = (
('ChildA', 'Child A'),
('ChildB', 'Child B'),
)
name = models.CharField(max_length=50)
child_type = models.CharField(choices=CHILD_CHOICES, max_length=25, blank=True)
class ChildA(models.model):
parent = OneToOneField('Parent',related_name='child_a',blank=True, null=True)
class ChildB(models.model):
parent = OneToOneField('Parent',related_name='child_b',blank=True, null=True)
When the parent is created the associated child model is created (depending on the child_type field).
How to retrieve the according Child type from a Parent in the Parent view in order to map a custom Form to the right child? (for editing the Parent and the right Child Type in the same view)
(In my real scenario I have 10 different child type)
Two options come to mind.
1) You can use the get_model() method, which is probably cleaner, to find the correct child.
2) You can do an IF statement in the middle of of a queryset. I have hardcoded the example, but you can probably pull the child_type variable all the way thru, and use annotate. I'm really just showing the example for how you can break up and tweak your queries with multiple related models.
Example 1:
from django.db.models import get_model
class ParentForm(forms.Form): # or view
***code here**
parent_model = Parent.objects.get(pk=1)
# Get model name from CHILD_CHOICES.
child_relation = [k for k, v in Parent.CHILD_CHOICES if v == parent_model.child_type]
child_model = get_model('app_name', child_relation)
child_model.objects.all()
***more code here***
Example 2: (occasionally handy depending on your setup):
class ParentManager(models.Manager):
def get_child_info(self, child_type):
try:
query = super(ParentManager, self).get_queryset()
# This line only works if the models have the same fields, otherwise do the below.
query = query.values('id', 'name')
if child_type == 'Child A':
# Do this if your child models have different fields.
query = query.values('id', 'name', 'unique_childA_field')
query = query.select_related('child_a').all()
elif child_type == 'Child B':
# Do this if your child models have different fields.
query = query.values('id', 'name', 'unique_childB_field')
query = query.select_related('child_b').all()
except Exception:
query = None
return query
class Parent(models.model):
CHILD_CHOICES = (
('ChildA', 'Child A'),
('ChildB', 'Child B'),
)
name = models.CharField(max_lenght=50)
child_type = models.CharField(choices=CHILD_CHOICES, max_length=25, blank=True)
objects = ParentManager()
then in your view or form:
class ParentForm(forms.Form): # or view
***code here**
child_info = Parent.objects.get_child_info('Child A')
***more code here***

Django MPTT tree as model filter in admin

I have a model linked to a related model that is a Django MPTT tree model, I would like to be able to filter the first model using the Django MPTT tree in the admin console.
class Tenders(models.Model):
...
sector=models.ForeignKey(Sector, to_field='sectorId', null=True, blank=True,on_delete=models.CASCADE)
...
class Sector(MPTTModel):
name = models.CharField(max_length = 255)
parent = TreeForeignKey('self', on_delete=models.CASCADE, null=True, blank=True,related_name='children')
sectorId = models.IntegerField(default=0,null=True,unique=True)
In the Django admin I would like to set up the filters for the Tenders model such that the Django-MPTT tree is the filter.
I have tried using the following:
class adminTenders(admin.ModelAdmin):
def linkTo(self,obj):
return mark_safe("""<a href='{}' target="_blank" >Tender Link</a>""".format(obj.tenderLink))
linkTo.short_description=''
list_display=(
'title',
'linkTo',
'sector',
'region',
'repository',
'id',
)
list_filter=(
('sector', TreeRelatedFieldListFilter),
)
admin.site.register(Tenders,adminTenders)
However I get the following error when trying to run this and I cant figure it out:
File "py36/lib/python3.6/site-packages/mptt/admin.py", line 314, in field_choices
mptt_level_indent * levels_dict[pk])
KeyError: 0
Any help would be greatly appreciated.
Edit 1: I think I have isolated the issue to the fact that my foreign key in Tenders to Sectors uses a to_field='sectorId instead of the default to link to the pk column. This had to be done for backwards compatibility to an old database scheme that I am stuck with.
So it turns out this is a bug in the django-mptt code for the field_choices function in the TreeRelatedFieldListFilter class.
To fix it I had to subclass and over ride that function to use the to_field that I had defined.
Here is the custom code:
class TreeRelatedForSectors(TreeRelatedFieldListFilter):
# Modified from django-mptt code to fix to_field problem
def field_choices(self, field, request, model_admin):
mptt_level_indent = getattr(model_admin, 'mptt_level_indent', self.mptt_level_indent)
language_bidi = get_language_bidi()
initial_choices = field.get_choices(include_blank=False)
pks = [pk for pk, val in initial_choices]
models = field.related_model._default_manager.filter(sectorId__in=pks)
levels_dict = {model.sectorId: getattr(model, model._mptt_meta.level_attr) for model in models}
choices = []
for pk, val in initial_choices:
padding_style = ' style="padding-%s:%spx"' % (
'right' if language_bidi else 'left',
mptt_level_indent * levels_dict[pk])
choices.append((pk, val, mark_safe(padding_style)))
return choices

django choice field returning index but not actual name

my models.py
class WebFieldType(models.Model):
WEBFIELD_CHOICES = ( ('FACEBOOK', 'fb'), ('INSTAGRAM', 'Insta') )
webfield_type = models.CharField(null=True, blank=True, max_length=30, choices=WEBFIELD_CHOICES)
def __unicode__(self):
return '{}'.format(self.webfield_type)
class WebTokens(models.Model):
token_name = models.TextField()
web_field_type = models.ManyToManyField(WebFieldType)
def __unicode__(self):
return '{}-{}'.format(self.token_name,self.web_field_type )
Now in shell if I do like:
WebFieldType.objects.all().values('webfield_type'), it is returning [{'webfield_type' : 'FACEBOOK'}, {'webfield_type' : 'INSTAGARAM'}]
But if I do like
WebTokens.objects.all().values('token_name','web_field_type') it is returning
[{'token_name' : 'some_token_name', 'web_field_type' : 1}]
As you can see in web_field_type it is returning id, but I need name.
Even tried using return '{}'.format(self.get_webfield_type_display()) in WebTokens model but in vain.
So basically I need everything that is stored in WebTokens model when I make a call to it.
You did not specify what is template_name attribute in the WebTokens model. But I'll try to get your point. What are doing is that you take the values from ManyToMany field which is in your case web_field_type: 1. So if you want to get all vallues from the model and serialize it, then it would be more complicated than this. Consider creating method in WebTokens model.
EDITED
#property
def web_field_type_types(self):
names = list()
for obj in self.web_field_type.all():
names.append(obj.get_type_display())
return str(names)
And then using it in your queries so you can get string of ids with
webtoken.web_field_type_types

Filtering queryset if one value is greater than another value

I am trying to filter in view my queryset based on relation between 2 fields .
however always getting the error that my field is not defined .
My Model has several calculated columns and I want to get only the records where values of field A are greater than field B.
So this is my model
class Material(models.Model):
version = IntegerVersionField( )
code = models.CharField(max_length=30)
name = models.CharField(max_length=30)
min_quantity = models.DecimalField(max_digits=19, decimal_places=10)
max_quantity = models.DecimalField(max_digits=19, decimal_places=10)
is_active = models.BooleanField(default=True)
def _get_totalinventory(self):
from inventory.models import Inventory
return Inventory.objects.filter(warehouse_Bin__material_UOM__UOM__material=self.id, is_active = true ).aggregate(Sum('quantity'))
totalinventory = property(_get_totalinventory)
def _get_totalpo(self):
from purchase.models import POmaterial
return POmaterial.objects.filter(material=self.id, is_active = true).aggregate(Sum('quantity'))
totalpo = property(_get_totalpo)
def _get_totalso(self):
from sales.models import SOproduct
return SOproduct.objects.filter(product__material=self.id , is_active=true ).aggregate(Sum('quantity'))
totalso = property(_get_totalpo)
#property
def _get_total(self):
return (totalinventory + totalpo - totalso)
total = property(_get_total)
And this is line in my view where I try to get the conditional queryset
po_list = MaterialFilter(request.GET, queryset=Material.objects.filter( total__lte = min_quantity ))
But I am getting the error that min_quantity is not defined
What could be the problem ?
EDIT:
My problem got solved thank you #Moses Koledoye but in the same code I have different issue now
Cannot resolve keyword 'total' into field.Choices are: am, author, author_id, bom, bomversion, code, creation_time, description, id, inventory, is_active, is_production, itemgroup, itemgroup_id, keywords, materialuom, max_quantity, min_quantity, name, pomaterial, produce, product, slug, trigger_quantity, uom, updated_by, updated_by_id, valid_from, valid_to, version, warehousebin
Basically it doesn't show any of my calculated fields I have in my model.
Django cannot write a query which is conditioned on a field whose value is unknown. You need to use a F expression for this:
from django.db.models import F
queryset = Material.objects.filter(total__lte = F('min_quantity'))
And your FilterSet becomes:
po_list = MaterialFilter(request.GET, queryset = Material.objects.filter(total__lte=F('min_quantity')))
From the docs:
An F() object represents the value of a model field or annotated
column. It makes it possible to refer to model field values and
perform database operations using them without actually having to pull
them out of the database into Python memory