list Box Django Forms - python

I'm using Django forms and need to create a list box.
What would be the equivalent of listbox in Django form fields?
I checked the documentation #
https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelchoicefield
but unable to find it.
Here is my code snippet,
Models.py
class Volunteer(models.Model):
NO_OF_HRS = (('1','1')
('2','2'))
datecreated = models.DateTimeField()
volposition = models.CharField(max_length=300)
roledesc = models.CharField(max_length=300)
Duration = models.CharField(choices=NO_OF_HRS,max_length=1)**
forms.py
class VolunteerForm(forms.ModelForm)
datecreated = forms.DateField(label=u'Creation Date')
volposition = forms.CharField(label=u'Position Name', max_length=300)
roledesc = forms.roledesc(label=u'Role description',max_length=5000)
Duration = forms.CharField(widget=forms.select(choices=NO_OF_HRS),max_length=2)
When I try to run, I get the following error,
NO_OF_HRS is not defined

Your NO_OF_HRS tuple is defined inside the model and not available to the form. It has to be imported in forms.py just like any other Python object. Try moving the tuple outside the model definition and import in your forms.py like this:
models.py
NO_OF_HRS = (('1','1')
('2','2'))
class Volunteer(models.Model):
# ...
duration = models.CharField(choices=NO_OF_HRS, max_length=1)
forms.py
from path.to.models import NO_OF_HRS
class VolunteerForm(forms.Form):
# ...
duration = forms.CharField(widget=forms.Select(choices=NO_OF_HRS), max_length=1)
It also looks like you want to use a ModelForm. In this case you don't need to add any field definitions to your VolunteerForm, simply set your model in the inner Meta class.
forms.py
from path.to.models Volunteer
class VolunteerForm(forms.ModelForm):
class Meta:
model = Volunteer

Related

Three-level inline formsets in the frontend

I'm trying to accomplish a three-level stacked inline form in Django. Suppose these models:
class Anuncio(models.Model):
title = models.CharField(max_length=200)
delivery = models.CharField(max_length=100)
class Product(models.Model):
anuncio = models.ForeignKey(Anuncio, on_delete=models.CASCADE)
name = models.CharField(max_length=200)
quantity = models.PositiveIntegerField(default=1)
price = models.PositiveIntegerField()
class Image(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
image = models.ImageField()
There is a relation Anuncio-Product and another relation Product-Image. With this Django package, I accomplished exactly what I want in the Django admin: when creating an Anuncio object, I can add as many Products as I want, and those products can have as many Images as I want. I'm trying to accomplish this in the front end.
I think the way to go is with Django formsets, but I'm facing some problems. All the resources I've been able to find online are only 'two-level' formsets or in 'three-level' cases all the foreign keys point to the same parent model.
With this forms.py file:
class ProductForm(ModelForm):
class Meta:
model = Product
fields = ['name', 'quantity', 'price']
class ImageForm(ModelForm):
class Meta:
model = Imagen
fields = ['image']
class AnuncioForm(ModelForm):
class Meta:
model = Anuncio
fields = ['title', 'delivery']
And this views.py function:
def anunciocreateview(request):
form = AnuncioForm(request.POST or None)
ProductFormSet = inlineformset_factory(Anuncio, Product, form=ProductForm)
ImageFormSet = inlineformset_factory(Product, Image, form=ImageForm)
if all([form.is_valid(), ProductFormSet.is_valid(), ImageFormSet.is_valid()]):
parent = form.save(commit=False)
parent.anunciante = request.user
parent.save()
for form in ProductoFormSet:
child = form.save(commit=False)
child.anuncio = parent
child.save()
for form in ImagenFormSet:
imagen = form.save(commit=False)
imagen.product = form.product
imagen.save()
context = {
'form_1' : form,
'form_2' : ProductFormSet,
'form_3' : ImageFormSet,
}
But I think I'm missing important points when it comes to add the proper relations between models. This set-up gives an AttributeError of: 'ProductForm' object has no attribute '__name__'
The, for example, 'add (extra) Product' that appears in AdminStackedInLine I guess it can be accomplished with JavaScript, playing with hidden forms and changing attributes on click events.
Anyone has experience doing something similar or can guide me through the correct direction? Also on how to manage the data and the relations of the submitted forms?
I think your problem is you have tried to validate a class Form instead of instanciate your formset and validate them.
Your code would be look like to something like that :
def anunciocreateview(request):
ProductFormSet = inlineformset_factory(Anuncio, Product, form=ProductForm)
ImageFormSet = inlineformset_factory(Product, Image, form=ImageForm)
anuncio_form = AnuncioForm(request.POST or None)
product_formset = ProductFormSet(request.POST or None)
image_formset = ImageFormSet(request.POST or None)
if all([form.is_valid(), product_formset.is_valid(), image_formset.is_valid()]):
...
The function inlineformset_factory just create a Form class, not a instance of form.
More information and example on the documentation : https://docs.djangoproject.com/fr/4.1/topics/forms/formsets/

Pulling several Django models together into a single list

I have a MySQL database with four related tables: project, unit, unit_equipment, and equipment. A project can have many units; a unit can have many related equipment entries. A single unit can only belong to one project, but there is a many-to-many between equipment and unit (hence the unit_equipment bridge table in the DB). I'm using Django and trying to create a view (or a list?) that shows all 3 models on the same page, together. So it would list all projects, all units, and all equipment. Ideally, the display would be like this:
Project --------- Unit ------------- Equipment
Project 1 first_unit some_equipment1, some_equipment2
Project 1 second_unit more_equipment1, more_equipment2
Project 2 another_unit some_equipment1, more_equipment1
Project 2 and_another_unit some_equipment2, more_equipment2
but at this point I'd also be happy with just having a separate line for each piece of equipment, if comma-separating them is a pain.
Although it seems straightforward to create a form where I can add a new project and add related unit and equipment data (using the TabularInline class), I cannot for the life of me figure out how to bring this data together and just display it. I just want a "master list" of everything in the database, basically.
Here's the code I have so far:
models.py
class Project(models.Model):
name = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'project'
def __str__(self):
return self.name
class Unit(models.Model):
project = models.ForeignKey(Project, models.DO_NOTHING, blank=True, null=True)
name = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'unit'
def __str__(self):
return self.name
class UnitEquipment(models.Model):
unit = models.ForeignKey(Unit, models.DO_NOTHING, blank=True, null=True)
equipment = models.ForeignKey(Equipment, models.DO_NOTHING, blank=True, null=True)
class Meta:
managed = False
db_table = 'unit_equipment'
class Equipment(models.Model):
name = models.CharField(max_length=100, blank=True, null=True)
description = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'equipment'
def __str__(self):
return self.name
views.py
def project_detail_view(request):
obj = Project.objects.all()
context = {'object': obj}
return render(request, "project/project_detail.html", context)
urls.py
urlpatterns = [
path('project/', project_detail_view),
path('', admin.site.urls),
]
admin.py
class UnitTabularInLine(admin.TabularInline):
model = Unit
extra = 0
class ProjectAdmin(admin.ModelAdmin):
inlines = [UnitTabularInLine]
class Meta:
model = Project
# a list of displayed columns name.
list_display = ['name']
# define search columns list, then a search box will be added at the top of list page.
search_fields = ['name']
# define filter columns list, then a filter widget will be shown at right side of list page.
list_filter = ['name']
# define model data list ordering.
ordering = ('name')
I think I need to somehow add more entries to the list_display in the admin file, but every time I try to add unit or equipment it throws an error. I've also tried adding more attributes to Project, but I can't seem to get the syntax right, and I'm never sure which model class I'm supposed to make it.
I've also looked at FormSets, but I cannot get my head around how to alter my current code to get it to work.
How do I get these models together into a unified view?
You don't need to edit the admin view to add your own view: which you may find you are able to do in this case to get your data displayed exactly as you want.
If you do want to show the related object values in the admin list, then you can use lookups and custom columns: however in this case your list would be based upon the Unit.
# You don't need an explicit UnitEquipment model here: you can
# use a simple ManyToManyField
class Unit(models.Model):
project = ...
name = ...
equipment = models.ManyToManyField(Equipment, related_name='units')
def equipment_list(admin, instance):
return ', '.join([x.name for x in instance.equimpent.all()])
class UnitAdmin(admin.ModelAdmin):
class Meta:
model = Unit
list_display = ['project__name', 'name', equipment_list]
def get_queryset(self, request):
return super().get_queryset(request)\
.select_related('project')\
.prefetch_related('equipment')
Note that you need to have the queryset override, otherwise there will be a bunch of extra queries as each unit also requires fetching the project and list of equipment for that unit.
There's also a further improvement you can make to your queries: you could aggregate the related equipment names using a Subquery annotation, and prevent the second query (that fetches all related equipment items for the units in the queryset). This would replace the prefetch_related()
Thanks to #Matthew Schinckel, I was able to find my way to the answer. Here's what my files look like now (only edited the Unit class in models.py):
models.py
class Unit(models.Model):
project = models.ForeignKey(Project, models.DO_NOTHING, blank=True, null=True)
name = models.CharField(max_length=255, blank=True, null=True)
equipment = models.ManyToManyField(Equipment, related_name='units')
class Meta:
managed = False
db_table = 'unit'
def __str__(self):
return self.name
def equipment_list(self):
return ', '.join([x.name for x in self.equipment.all()])
admin.py
class UnitAdmin(admin.ModelAdmin):
class Meta:
model = Unit
# a list of displayed columns name.
list_display = ('project', 'name', 'equipment_list')
# define search columns list, then a search box will be added at the top of list page.
search_fields = ['project']
# define filter columns list, then a filter widget will be shown at right side of list page.
list_filter = ['project', 'name']
# define model data list ordering.
ordering = ('project', 'name')
def get_queryset(self, request):
return super().get_queryset(request)\
.select_related('project')\
.prefetch_related('equipment')
So the changes I made were:
1. Make list_display a tuple instead of a list.
2. Throw def equipment_list(self) into the Unit class (so it's callable as an attribute of Unit) and pass (self) instead of (admin, instance) (I kept getting an error that was looking for the instance argument).

Django Python - How to add foreign key to table displayed in admin page

So I got 4 tables in a MySQL database.
The database is managed with Django under an app I created with Django.
Here are 2 of the 4 classes in the file models.py corresponding to the database tables:
from __future__ import unicode_literals
from django.db import models
class Pdb(models.Model):
id_pdb_chain = models.CharField(db_column='id_PDB_chain', primary_key=True, max_length=5) # Field name made lowercase.
id_pdb = models.CharField(db_column='id_PDB', max_length=4) # Field name made lowercase.
chaine = models.CharField(max_length=10)
header = models.CharField(max_length=255)
sequence_proteine = models.TextField(db_column='sequence_Proteine') # Field name made lowercase.
start_seq = models.IntegerField()
taille_proteine = models.IntegerField(db_column='taille_Proteine') # Field name made lowercase.
resolution_pdb = models.FloatField(db_column='resolution_PDB') # Field name made lowercase.
meth_res = models.ForeignKey('MethodesRes', models.DO_NOTHING, db_column='meth_Res') # Field name made lowercase.
def __unicode__(self):
return self.id_pdb
class Meta:
managed = False
db_table = 'PDB'
class StructSec(models.Model):
id_struct_sec = models.AutoField(primary_key=True)
start_pred = models.IntegerField()
structure_predite = models.TextField(db_column='structure_Predite') # Field name made lowercase.
nombre_ppii = models.IntegerField(db_column='nombre_PPII') # Field name made lowercase.
pourcentage_ppii = models.FloatField(db_column='pourcentage_PPII') # Field name made lowercase.
angle_phi = models.TextField()
angle_psi = models.TextField()
id_pdb_chain = models.ForeignKey(Pdb, models.DO_NOTHING, db_column='id_PDB_chain') # Field name made lowercase.
nom_analyse = models.ForeignKey(MethodesAnalyse, models.DO_NOTHING, db_column='nom_Analyse') # Field name made lowercase.
def __unicode__(self):
return self.structure_predite
class Meta:
managed = False
db_table = 'struct_sec'
As you can see I already found a way to display a field for each table in the admin page with this part in each table:
def __unicode__(self):
return self.structure_predite
But when I want to replace "structure_predite" this by the primary key in the "StructSec" class like this:
def __unicode__(self):
return self.id_struct_sec
Django return this error:
TypeError: coercing to Unicode: need string or buffer, long found
I also have to mention that I have made few modifications in the "admin.py" file here it is:
from django.contrib import admin
from .models import Pdb, MethodesAnalyse, MethodesRes, StructSec
class PdbInline(admin.TabularInline):
model = Pdb
class PdbAdmin(admin.ModelAdmin):
list_display = ('id_pdb','header','chaine','taille_proteine','meth_res')
list_filter = ['chaine','meth_res']
search_fields = ['id_pdb_chain','header']
class MethodesAnalyseInline(admin.TabularInline):
model = MethodesAnalyse
class MethodesAnalyseAdmin(admin.ModelAdmin):
list_display = ('nom_analyse')
class MethodesResInline(admin.TabularInline):
model = MethodesRes
class MethodesResAdmin(admin.ModelAdmin):
list_display = ('meth_res')
class StructSecInline(admin.TabularInline):
model = MethodesRes
class StructSecAdmin(admin.ModelAdmin):
list_display = ('id_struct_sec','nombre_PPII','pourcentage_PPII','id_PDB','nom_Analyse')
search_fields = ['nombre_ppii','pourcentage_ppii']
admin.site.register(Pdb,PdbAdmin)
admin.site.register(MethodesAnalyse)
admin.site.register(MethodesRes)
admin.site.register(StructSec)
Knowing this, my question is simple:
How to display the autoincremented IDs (Primary Key) from the class StructSec in the corresponding admin table without errors?
I think the error is raising due to None value. Python cannot convert None.
Replace your unicode definition to 'str' as:
def __str__(self):
return self.id_struct_sec
Sijan answer is correct, I will just add that, to make links work, you may also have to do this:
def __str__(self):
return str(self.id_struct_sec)
Without the "str()" in the return, links don't work and return errors when clicked.

django-tables2 custom column

I'm working now on my first Django project. I want to render results table which contains all fields from Priekabos model and one custom column from Grafikas which should contain something similar to:
SELECT max(kada_moketi) FROM grafikas WHERE priekabos_id = ?
Whatever I try from examples nothing works. Should I write another view function with that custom query:
(Grafikas.objects.filter(priekabos_id=1)
neither with:
.aggregate(Max('kada_moketi')
neither with:
.latest('kada_moketi')
worked for me I created a new table class in tables.py which later PriekabosTable will inherit? That didn't work for me too.
Here's my code:
models.py
class Grafikas(models.Model):
id = models.AutoField(primary_key=True)
mokejimo_nr = models.IntegerField()
kada_moketi = models.DateField()
priekabos = models.ForeignKey('Priekabos', models.DO_NOTHING)
class Priekabos(models.Model):
id = models.AutoField(primary_key=True)
sutarties_nr = models.CharField(unique=True, max_length=45, verbose_name='Sut. Nr.')
nuomos_pradz = models.DateField()
sutarties_trukme = models.IntegerField()
views.py
def priekabos_table(request):
table = PriekabosTable(Priekabos.objects.all())
RequestConfig(request, paginate={'per_page': 20}).configure(table)
return render(request, 'isperkamoji_nuoma/priekabos_table.html', {'table': table})
tables.py
class PriekabosTable(tables.Table):
class Meta:
model = Priekabos
attrs = {"class": "paleblue"}
fields = ('id', 'sutarties_nr', 'nuomos_pradz')
For better understanding, here's 'grafikas' table:
MySQL 'grafikas' table
It sounds like you might be able to fetch the extra field using annotate.
from django.db.models import Max
queryset = Priekabos.objects.annotate(max_kada_moketi=Max('grafikas__kada_moketi'))
table = PriekabosTable(queryset)
Remember to add the field to your table.
class PriekabosTable(tables.Table):
class Meta:
model = Priekabos
attrs = {"class": "paleblue"}
fields = ('id', 'sutarties_nr', 'nuomos_pradz', 'max_kada_moketi')

Searching by related fields in django admin

I've been looking at the docs for search_fields in django admin in the attempt to allow searching of related fields.
So, here are some of my models.
# models.py
class Team(models.Model):
name = models.CharField(max_length=255)
class AgeGroup(models.Model):
group = models.CharField(max_length=255)
class Runner(models.Model):
"""
Model for the runner holding a course record.
"""
name = models.CharField(max_length=100)
agegroup = models.ForeignKey(AgeGroup)
team = models.ForeignKey(Team, blank=True, null=True)
class Result(models.Model):
"""
Model for the results of records.
"""
runner = models.ForeignKey(Runner)
year = models.IntegerField(_("Year"))
time = models.CharField(_("Time"), max_length=8)
class YearRecord(models.Model):
"""
Model for storing the course records of a year.
"""
result = models.ForeignKey(Result)
year = models.IntegerField()
What I'd like is for the YearRecord admin to be able to search for the team which a runner belongs to. However as soon as I attempt to add the Runner FK relationship to the search fields I get an error on searches; TypeError: Related Field got invalid lookup: icontains
So, here is the admin setup where I'd like to be able to search through the relationships. I'm sure this matches the docs, but am I misunderstanding something here? Can this be resolved & the result__runner be extended to the team field of the Runner model?
# admin.py
class YearRecordAdmin(admin.ModelAdmin):
model = YearRecord
list_display = ('result', 'get_agegroup', 'get_team', 'year')
search_fields = ['result__runner', 'year']
def get_team(self, obj):
return obj.result.runner.team
get_team.short_description = _("Team")
def get_agegroup(self, obj):
return obj.result.runner.agegroup
get_agegroup.short_description = _("Age group")
The documentation reads:
These fields should be some kind of text field, such as CharField or TextField.
so you should use 'result__runner__team__name'.

Categories