Django Form Field Not Setting Value After Submitting - python-3.x

I'm trying to post data from a form that contains incomplete data (I set the missing data in the View class before saving) for the following model. But the form does not get submitted as it is invalid (it's missing the harvest_amount, but I set the value on the webpage before submitting.
class Harvest(models.Model):
harvest_amount = models.IntegerField(validators=[MinValueValidator(limit_value=0)])
harvest_date = models.DateField()
harvest_for_plant = models.ForeignKey(Plant, on_delete=models.CASCADE)
and my form
class HarvestCreationForm(forms.ModelForm):
class Meta:
model = Harvest
fields = [
'harvest_amount'
]
def is_valid(self):
//check if Id in the url contains a valid id for a plant
return True

In this case I forgot to migrate my changes to the model (where a field was deleted). When the form posted the data it would always hit the not null constraint since the deleted field was not being set.

Related

Correct way to define the initial value of a form field in Django from a database, when database values may change

I am trying to create an interview feedback capture app using Django. The interview feedbacks follow a template. The template would evolve with time. Therefore whenever a new interview feedback template is available, it is updated to the database by an admin user.
Whenever an interviewer opens the app, he should see the latest value of the template available in the database as the initial value of the feedback form.
Currently I am able to provide the initial value of the feedback field using the 'initial' argument of the feedback field.
Below is the code (I am interested in the R1Form):
from django import forms
from .models import R1
from .model_templates import template_R1
from ckeditor.widgets import CKEditorWidget
class DateInput(forms.DateInput):
input_type = 'date'
class R1Form(forms.ModelForm):
interview_date = forms.DateField(widget = DateInput())
feedback = forms.CharField(widget = CKEditorWidget(), initial = template_R1.objects.all().last().template)
class Meta:
model = R1
fields = ['interview_date', 'interviewers', 'comment', 'recommended_level', 'progress']
The problem with this approach if that if the template is updated the form field still shows an earlier snapshot of the template when the django server was started.
Is there any other way the form field would show dynamic values as soon as the template is updated in the database? I believe somehow if the initial value could be passed from the views.py, then this could be resolved?
Okay so, I found an elegant solution.
I used the init method of the form class to initialize the feedback field:
class R1Form(forms.ModelForm):
interview_date = forms.DateField(widget=DateInput())
feedback = forms.CharField(widget=CKEditorWidget())
class Meta:
model = R1
fields = ['interview_date', 'interviewers', 'feedback', 'comment', 'recommended_level', 'progress']
And from the views, I used the get_initial method to pass the initial value of the form as the template value from the database.
class R1CreateView(LoginRequiredMixin, UserPassesTestMixin, CreateView):
model = R1
template_name = "interviews/r1/create.html"
def get_initial(self):
"""Return the initial data to use for forms on this view."""
return {'feedback': template_R1.objects.all().last().template}

How to override field name attribute in django model form?

I have a model form similar to the one below:
class BookSearchForm(ModelForm):
class Meta:
model = Book
fields = ['publisher', 'authors', 'category'
How to override fields name attribute in the above model form?
I tried this, but it did not work:
class BookSearchForm(ModelForm):
class Meta:
model = Book
fields = ['publisher', 'authors', 'category'
widgets = {
'publisher': forms.SelectMultiple(attrs={'name': 'pub'}),
'authors': forms.SelectMultiple(attrs={'name': 'aut'}),
'category': forms.SelectMultiple(attrs={'name': 'cat'}),
}
you don't want to change the name of a field in the forms, django needs that to collect data for that field if you don't provide db_column as the name of the db column. what you can do with the first option, if you want the user to see publisher or some other name, in the models, add a verbose_name and then for the actual name you can declare the field however you want. Your code could look like this
pub = models.WhateverField(verbose_name='what i want you to see')
now when you do {{form.pub.label)}}, 'what i want you to see' is displayed in the html. Of course, don't forget to add the actual input in you template, {{form.pub}}. This way, you don't add anything extra in the form to display a user friendly name. I've posted this as an answer as i ran out of characters for a commment.

exiting lot_id in stock.move.line, showing same value from compute field

I got some issue that lot_name is not showing same value from another compute field in same recordset, stock.move.line. below is my code:
class StockMoveLine(models.Model):
_name = 'stock.move.line'
_inherit = 'stock.move.line'
lotter = fields.Char(string="Supplier Lot", compute='_compute_partner')
def _compute_partner(self):
if not self.lotter:
partner_id = self.env['stock.picking'].search([('name','=',self.reference)]).partner_id.id
self.lotter = str(partner_id)
if self.lot_name == "":
self.lot_name = self.lotter
else:
self.lot_name = "blank"
the lot_name had been already existed in base module field. So I would like to show partner_id in lot_name field as well. now I only see it in my new compute fieldl. I tried using #api.onchange but it is only work when the textfield of lotter is lost focus. how can I do to show the same value on both lotter and lot_name fields if there is no value earlier?
Add store=True in your field.Because compute field is not stored in your database.
lotter = fields.Char(string="Supplier Lot", compute='_compute_partner', store=True)

Django Admin - Include field of a model A when adding a record of model B

I'm working on legacy code which is in Django (1.11)
I have a model A, with attributes:
Model_A:
Name (NOT NULL)
City (NOT NULL)
FieldX (Nullable) - CharField
And a model B, with attributes:
Model_B:
Name (NOT NULL)
City (NOT NULL)
RelatedField (ForeignKey to an instance of Model_A)
Now, When I add a record for Model_A then I may NOT need to fill FieldX.
However, When I add a record for Model_B then I'll have to select an instance of Model_A and then if FieldX of that instance is NULL then I have to fill that as well (make it mandatory).
The form for Model_A is pretty straight forward.
But for Model_B I need a form where:
First an instance of Model_A is selected (Dropdown)
The input box for FieldX of instance selected in 1 is shown (Editable and mandatory to fill, blank=False).
The rest of the fields are shown (Name, City, FieldY).
Can this be done using the admin page? Or will I have to create proper forms and user flow for this?
I have not tested this, but you should be able to do the following:
from django.contrib import admin
class ModelAForm(ModelForm):
FieldX = CharField(
required=True
)
class Meta:
model = Model_A
fields = ['FieldX']
class ModelAInline(admin.StackedInline):
model = Model_A
form = ModelAForm
class ModelBAdmin(admin.ModelAdmin):
inlines = [
ModelAInline
]
admin.site.register(ModelB, ModelBAdmin)
For model B I'd override the view and form in order to achieve this in admin site.
model_b_view = ModelBView.as_view()
#admin.register(Model_B)
class Model_B_Admin(admin.ModelAdmin):
def get_urls(self):
urls = super().get_urls()
my_urls = [
url(r'^add/$',
self.admin_site.admin_view(model_b_view)),
]
return my_urls + urls
Where ModelBView is a template view, and you'd have to override get and post of it, In get send all instances of Model_A to template and handle via jquery there.

Web2Py list:reference table, Load and set data

Maybe I'm missing something absurd, I'm not seeing, but this is my first app to study web2py.
I am unable to enter the data in Table Movies, which has fields related to other tables.
The list is loaded, but it is not registered in Movies registration.
Under the codes and the results.
db.py
Movie = db.define_table('movies',
Field('title','string', label = 'Title'),
Field('date_release','integer', label = 'Date Release'),
Field('duraction','integer', label = 'Duraction'),
Field('category','string','list:reference categories', label = 'Category'),
Field('actor','list:reference actors', label = 'Actor'),
Field('director','list:reference directors', label = 'Diretor'),
)
Category = db.define_table('categories',
Field('title','string', label = 'Title'),
)
validators.py
Movie.title.requires = [IS_NOT_EMPTY(), IS_NOT_IN_DB(db, 'movies.title')]
Movie.category.requires = IS_IN_DB(db, 'categories.title')
Movie.director.requires = IS_IN_DB(db, 'directors.name')
Movie.actor.requires = IS_IN_DB(db, 'actors.name')
Movie.duraction.requires = IS_INT_IN_RANGE(0, 1000)
Category.title.requires = IS_NOT_EMPTY()
movie.py
def add():
form = SQLFORM(Movie)
if form.process().accepted:
response.flash = "Successful! New movie added!"
redirect(URL('add'))
elif form.errors:
response.flash = 'Error'
else:
response.flash = 'Form, set data'
return dict(form = form)
List Load another tables - ok:
The items of list not record in DB:
The widgets displayed in the form are based on the IS_IN_DB field validators you have specified, and there are three problems with the way you have coded them.
First, list:reference fields, like standard reference type fields, store the record IDs of the records they reference -- they do not store values of other fields within the referenced records. So, the second argument to the IS_IN_DB validator should always be the ID field (e.g., categories.id).
Second, although the field will store record IDs, you want the form widget to show some other more descriptive representation of each record, so you should specify the "label" argument of the IS_IN_DB validator (e.g., label='%(title)s').
Third, list:reference fields allow for multiple selections, so you must set the "multiple" argument of the IS_IN_DB validator to True. This will result in a multi-select widget in the form.
So, the resulting validator should look like this:
Movie.category.requires = IS_IN_DB(db, 'categories.id', label='%(title)s', multiple=True)
The above will allow multiple db.categories IDs to be selected, though the form widget will display category titles rather than the actual IDs.
Now, all of the above can be made much easier if you instead define the referenced tables before the db.movies table and specify a format argument for each table:
Category = db.define_table('categories',
Field('title','string', label = 'Title'),
format='%(title)s')
Movie = db.define_table('movies',
...,
Field('category', 'list:reference categories', label = 'Category'),
...)
With the above code, there is no need to explicitly specify the IS_IN_DB validator at all, as the db.movies.category field will automatically get a default validator exactly like the one specified above (the format attribute of the db.categories table is used as the label argument).
You might want to read the documentation on list:reference fields and the IS_IN_DB validator.
As an aside, you might consider specifying your field validators within the table definitions (via the requires argument to Field()), as this is more concise, keeps all schema-related details in one place, and eliminates the need to read and execute an additional model file on every request.

Resources