Custom Form Field Naming Within Loop - python

I'm trying to specify field names within a loop on a form, so that the fields do not overwrite themselves. Right now I'm using this in my form:
class TicketForm(forms.ModelForm):
class Meta:
model = Ticket
exclude = ['products']
class ProductForm(forms.Form):
product_id = forms.IntegerField()
count = forms.IntegerField()
class TicketProductForm(forms.ModelForm):
class Meta:
model = Ticket
exclude = []
for Product in Product.objects.all():
product_id = forms.IntegerField(initial=Product.product_id)
count = forms.IntegerField(initial=0)
The for loop within TicketProductForm currently only displays one set of fields, as product_id and count overwrite the fields over and over, only displaying for the last entry in the set of Product.
Is there a way to customize field names so that I can name them, preferably with a way I can set a specifier to allow easier lookup later?

Related

How to fetch related model in django_tables2 to avoid a lot of queries?

I might be missing something simple here. And I simply lack the knowledge or some how-to.
I got two models, one is site, the other one is siteField and the most important one - siteFieldValue.
My idea is to create a django table (for site) that uses the values from siteFieldValue as a number in a row, for a specific site, under certain header. The problem is - each site can have 50s of them. That * number of columns specified by def render_ functions * number of sites equals to a lot of queries and I want to avoid that.
My question is - is it possible to, for example, prefetch all the values for each site (SiteFieldValue.objects.filter(site=record).first() somewhere in the SiteListTable class), put them into an array and then use them in the def render_ functions by simply checking the value assigned to a key (id of the field).
Models:
class Site(models.Model):
name = models.CharField(max_length=100)
class SiteField(models.Model):
name = models.CharField(max_length=100)
description = models.CharField(max_length=500, null=True, blank=True)
def __str__(self):
return self.name
class SiteFieldValue(models.Model):
site = models.ForeignKey(Site, on_delete=models.CASCADE)
field = models.ForeignKey(SiteField, on_delete=models.CASCADE)
value = models.CharField(max_length=500)
Table view
class SiteListTable(tables.Table):
name = tables.Column()
importance = tables.Column(verbose_name='Importance',empty_values=())
vertical = tables.Column(verbose_name='Vertical',empty_values=())
#... and many more to come... all values based on siteFieldValue
def render_importance(self, value, record):
q = SiteFieldValue.objects.filter(site=record, field=1).first()
# ^^ I don't want this!! I would want the SiteFieldValue to be prefetched somewhere else for that model and just check the array for field id in here.
if (q):
return q.value
else:
return None
def render_vertical(self, value, record):
q = SiteFieldValue.objects.filter(site=record, field=2).first()
# ^^ I don't want this!! I would want the SiteFieldValue to be prefetched somewhere else for that model and just check the array for field id in here.
if (q):
return q.value
else:
return None
class Meta:
model = Site
attrs = {
"class": "table table-striped","thead" : {'class': 'thead-light',}}
template_name = "django_tables2/bootstrap.html"
fields = ("name", "importance", "vertical",)
This might get you started. I've broken it up into steps but they can be chained quite easily.
#Get all the objects you'll need. You can filter as appropriate, say by site__name).
qs = SiteFieldValue.objects.select_related('site', 'field')
#lets keep things simple and only get the values we want
qs_values = qs.values('site__name','field__name','value')
#qs_values is a queryset. For ease of manipulation, let's make it a list
qs_list = list(qs_values)
#set up a final dict
final_dict = {}
# create the keys (sites) and values so they are all grouped
for site in qs_list:
#create the sub_dic for the fields if not already created
if site['site__name'] not in final_dict:
final_dict[site['site__name']] = {}
final_dict[site['site__name']][site['name']] = site['site__name']
final_dict[site['site__name']][site['field__name']] = site['value']
#now lets convert our dict of dicts into a list of dicts
# for use as per table2 docs
data = []
for site in final_dict:
data.append(final_dict[site])
Now you have a list of dicts eg,
[{'name':site__name, 'col1name':value...] and can add it as shown in the table2 docs

New object not added to queryset in Django

Here is the model
class Student(models.Model):
"""Student info"""
id = models.CharField( max_length=7,primary_key=True)
name = models.CharField(_('name'),max_length=8, default=""); # help_text will locate after the field
address = models.CharField(_('address'),max_length=30,blank=True,default="") #blank true means the
GENDER_CHOICES = [("M", _("male")),("F",_("female"))]
student_number = models.CharField(max_length=10,blank=True)
gender = models.CharField(_("gender"),max_length=6, choices = GENDER_CHOICES, default="M");
I user shell to create two users as below:
But the queryset number didn't increase although I created two users.
#I hate Django. Q = Q
You are using a custom id field as model id and you put it as Char, so you should add this id also when you want to save an object
But it is not a good idea, if you want to use custom id use it in this way
id = models.AutoField(primary_key=True)

How to make Django values() exclude some fields?

I have multiple models which has fields like "created_at" "updated_at" which I don't want to get with objects.values().
Does Django has any way to exclude fields in values()?
I know people refer to defer(), but it doesn't return QuerySet<Dict> like values() instead returns QuerySet<Model>.
I tried objects.defer("created_at", "updated_at").values(), but it includes those 2 deferred fields in the resulting Dict.
I see defer().query only selecting the non-exluded fields in the SQL, but using defer(..).values() resets the deferred fields and selects all fields.
I cannot specify which field I want, since different model has different fields, I can only specity which fields I don't want. So I cannot use values('name', 'age', ...)
I'm planning to use a CustomeManager, which I can use in all model.
Example:
class CustomManager(models.Manager):
def values_excluded(self):
return self.values() # somehow exlude the fields and return QuerySet<Dict>
class ExampleModel(models.Model):
name = models.CharField(max_length=20)
age = models.IntegerField()
created_at = models.DateTimeField()
updated_at = models.DateTimeField()
objects = CustomManager()
ExampleModel.objects.values_excluded()
Is there any way in Django or do I have to manually delete those keys from the resulting Dict from values()?
esclude_fields = ['created_at', 'updated_at']
keys = [f.name for f in Model._meta.local_fields if f.name not in esclude_fields]
queryset.values(*keys)
This should work:
class CustomManager(models.Manager):
def values_excluded(self, *excluded_fields):
included_fields = [f.name for f in self.model._meta.fields if f.name not in excluded_fields]
return self.values(*included_fields)

django change list display column name with foreign field value

I am using django 1.9 , i have following model which have two foreing fields,
models.py
class Mobile(models.Model):
name = models.CharField(max_length=100)
product = models.ForeignKey(Product)
owner = model.ForeignKey(Customer)
now i want to build a admin interface for Mobile model where i want to provide list display for both product and owner foreign fields,that is, the list display will show one of the field of that foreign fields and also want to change the column name,i have tried so but its not working at all
admin.py
class MobileModelAdmin(admin.ModelAdmin):
list_display = ('name','product', 'owner')
def related_product(self, obj):
return obj.product.name
related_product.short_description = 'product name'
def related_owner(self, obj):
return obj.owner.name
related_owner.short_description = 'owner name'
admin.site.register(Mobile, MobileModelAdmin)
but neither its changing the column name nor showing the related value.
I think you have to do
list_display = ('name','related_product', 'related_owner') #means method name what you are given.

django admin inline many to many custom fields

Hi I am trying to customize my inlines in django admin.
Here are my models:
class Row(models.Model):
name = models.CharField(max_length=255)
class Table(models.Model):
rows = models.ManyToManyField(Row, blank=True)
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
and my admin:
class RowInline(admin.TabularInline):
model = Table.rows.through
fields = ['name']
class TableAdmin(admin.ModelAdmin):
inlines = [
RowInline,
]
exclude = ('rows',)
However I get this error
ImproperlyConfigured at /admin/table_app/table/1/
'RowInline.fields' refers to field 'name' that is missing from the
form.
How is that possible ?
class RowInline(admin.TabularInline):
model = Table.rows.through
fields = ['name']
This presents a problem because Table.rows.through represents an intermediate model. If you would like to understand this better have a look at your database. You'll see an intermediate table which references this model. It is probably named something like apname_table_rows. This intermeditate model does not contain the field, name. It just has two foreign key fields: table and row. (And it has an id field.)
If you need the name it can be referenced as a readonly field through the rows relation.
class RowInline(admin.TabularInline):
model = Table.rows.through
fields = ['row_name']
readonly_fields = ['row_name']
def row_name(self, instance):
return instance.row.name
row_name.short_description = 'row name'
class TableAdmin(admin.ModelAdmin):
inlines = [
RowInline,
]
exclude = ('rows',)
Django can not display it as you expected. Because there is an intermediary join table which joins your tables. In your example:
admin.py:
class RowInline(admin.TabularInline):
model = Table.rows.through # You are not addressing directly Row table but intermediary table
fields = ['name']
As the above note, model in RowInline addressing following table in your database, not Your Row table and model
table: your-app-name_table_row
--------------------------------
id | int not null
table_id | int
row_id | int
You can think it like there is an imaginary table in your model that joins the two tables.
class Table_Row(Model):
table = ForeignKey(Table)
row = ForeignKey(Row)
So if you edit your Inline as following
class RowInline(admin.TabularInline):
model = Table.rows.through # You are not addressing directly Row table but intermediary table
fields = ['row', 'table']
you will not see any error or exception. Because your model in RowInline addresses an intermediary table and that table do have those fields. Django can virtualize the imaginary table Table_Row up to here and can handle this.
But we can use relations in admin, with using __. If your code do have a ForeignKey relation instead of ManyToManyField relation, then following be valid in your admin
class Row(models.Model):
name = models.CharField(max_length=255)
class Table(models.Model):
rows = models.ForeignKey(Row, blank=True)
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
and your admin:
class RowInline(admin.TabularInline):
model = Table
fields = ['rows__name']
Because you will have real Models and djnago can evaluate __ relation on them
But if you try that in your structure:
class Row(models.Model):
name = models.CharField(max_length=255)
class Table(models.Model):
rows = models.ManyToManyField(Row, blank=True)
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
and your admin:
class RowInline(admin.TabularInline):
model = Table.rows.through
fields = ['row__name']
it will raise Exception! Because you do not have real table in your model, and django can not evaluate __ relations on virtual models it designs on top of its head.
Conclusion:
In your Inlines addressing ManyToMany relations, you are dealing with an imaginary intermediary model and you can not use fields orexclude attributes on that because your imaginary model do not have those fields and django can not handle relations ober that imaginary table. Following will be acceptable
class RowInline(admin.TabularInline):
model = Table.rows.through
# No fields or exclude declarations in here
and django will display combo boxes for your virtual intermediary table options and add a fancy green + sign to add new records, but you will can not have inline fields to add new records directly to your database within the same single page. Djnago can not handle this on a single page.
You can try creating real intermediary table and show it using through, but thats totally a longer job and I do not test it to see its results.
Update: There is also the reason why django do not let something like that. Consider following:
Table | Table_Row | Row
-----------+-----------+---------
Start 5 | |
Step 1 5 | | 1
Step 2 5 | 5-1 | 1
At the beginning, You have a table with no related Rows, you want to add a row to the table... For joining a row with a table, you must first create a row so you execute step 1. After You do create your row, you can create Table_Row record to join these two. So in contains more than a single database insertion. Django crew may avoid such usage since it contains multiple inserts and operation is related more tables.
But this is just an assumption on the reason of the behavior.
In your admin.py try this
class RowInline(admin.TabularInline):
model = Table.rows.through
list_display = ('name',)
class TableAdmin(admin.ModelAdmin):
inlines = [
RowInline,
]
readonly_fields = ('rows',)

Categories