Django skip inline field - django

I have an inline where the first field is always selected by default. Since both are required, is it possible to only validate and insert/update when the second field is also selected or I need to also define a default value for the second field? (Otherwise I will always get errors on the rows where only the first field is set...)
Update
I'm overriding the first widget (TextInput) render. If I set to empty then it works but I want this field to behave almost like a label.
def render(self, name, value, attrs=None):
if name == "opinion_set-0-topic":
value = "first thing"
if name == "opinion_set-1-topic":
value = "second thing"
if name == "opinion_set-2-topic":
value = "third thing"
Update 2
I need something like, if field 2 isn't set (gives "This field is required.") then bypass the form error and simply ignore the rows where this happens...

The situation is:
you have a main form to save
you have an inline subform with mandatory fields, some with default values and some without default values
Then, if you don't set all mandatory values in the inline, you get "This field is required" error.
One way to skip this situation is defining "extra = 0" in the admin.py definition of the inline form.
This way you are not forced to insert an inline object along the main one, but you can do it if you want, just clicking "+ Add another <inline>".

Related

ModelChoiceField: remove empty option and select default value

I'm using the default form field for a models.ForeignKey field, which is a ModelChoiceField using the Select widget.
The related model in question is a Weekday, and the field was made nullable so that it didn't force a default value on hundreds of existing entries. However, in practice, our default should be Sunday, which is Weekday.objects.get(day_of_week=6).
By default the select widget for a nullable field when rendered displays the null option. How can I discard this option and have a default value instead?
If I set a initial value, that one is selected by default on a new form:
self.fields['start_weekday'].initial = Weekday.objects.get(day_of_week=6)
But the empty value is still listed. I tried overriding the widget choices:
self.fields['start_weekday'].widget.choices = [(wd.day_of_week, wd.name) for wd in Weekday.objects.all()]
However now Sunday isn't selected by default. I thought maybe I need to use the option value as the initial one but that didn't work either:
self.fields['start_weekday'].initial = Weekday.objects.get(day_of_week=6).pk
In short: how can I remove the empty option in a nullable model field and select a default instead?
Provide empty_label=None in ModelChoiceField
start_weekday = ModelChoiceField(Weekday.objects.filter(day_of_week=6), empty_label=None)
OR
instead of assigning initial, you can assign empty_label also
self.fields['start_weekday'].empty_label = None
OR
you can provide default value in field in models also
start_weekday = models.CharField(max_length=10,choices=[(wd.day_of_week, wd.name) for wd in Weekday.objects.all()],default=6)

How to access a field name in Django model via the verbose name or Column name

I have the verbose name or column name of a model and I would like to get the corresponding field name. I'm able to access all the field names by using _meta..., but I only want to access a particular field name based on the verbose name.
I would like to plug in the verbose name or column name and get back what the field name is. All the examples that I've found you can only enter the field name in the Model._meta.get_field('fieldname') and not the verbose name to get whatever the field name is.
res = Model._meta.get_field('test_field').verbose_name
res returns 'Test Field'
res = Model._meta.get_field('test_field').name
res returns 'test_field'
If I enter the verbose name for ex:
res = Model._meta.get_field('Test Field').name
I get an error raise FieldDoesNotExist KeyError" 'Test Field'
I would like the output to be the field name 'test_field'
A problem might be here that multiple fields can have the same verbose_name, hence it is not a good identifier. It is not the task of a verbose name to act as an identifier anyway, these are used, as the documentation says to:
A human-readable name for the field. If the verbose name isn't given, Django will automatically create it using the field's attribute name, converting underscores to spaces.
We can make a function that does this, like:
def get_field_from_verbose(meta, verbose_name):
try:
return next(f for f in _meta.get_fields() if f.verbose_name == verbose_name)
except:
raise KeyError(verbose_name)
We can let it work with multiple names of a field, like:
def get_field_from_verbose(meta, verbose_name):
try:
return next(
f for f in _meta.get_fields()
if f.verbose_name in (f.name, f.verbose_name, f.db_column)
)
except:
raise KeyError(verbose_name)
But this even looks more problematic, since now the database column name of one field can be equal to the verbose name of another field, making it even more non-sensical.
and thus call it with:
get_field_from_verbose(Model._meta, 'Test Field')
It will raise a KeyError as well, given no such field can be found.
After a user selects a check box that displays the verbose name I'm using that name to access the fieldname to use in a query.
If this is more the sake of user interface, you should attack the field name as value to the checkbox, and the verbose name as "label". Like for example:
<input type="checkbox" name="sel_fields[]" value="fieldname1">Verbose fieldname 1<br>
<input type="checkbox" name="sel_fields[]" value="fieldname2">Verbose fieldname 2<br>
<input type="checkbox" name="sel_fields[]" value="fieldname3">Verbose fieldname 3<br>
Here the user will thus see Verbose fieldname 1 for the first checkbox, but if the user selects that checkbox, and hits the submit button, the view will receive the fieldname1 value, so the real name of that field.
This is in essence what Django does when you for example use a ModelChoiceField in a form: it presents the items nicely, but behind the curtains, it passes the primary key as value, and thus when one submits the form, we receive the primary key of the selected item back, not the textual representation of that object.
I agree with Willem that there are issues here with reliability and determistic behaviour, but something like this would work:
{field.verbose_name: field for field in model._meta.get_fields()}['Test Field']

Django WebTest: Check/uncheck checkbox by value

I've got a form with a set of checkboxes (all under the same field name) in my WebTest response, and I'd like to uncheck some of them according to their value. I've tried this:
my_form = response.forms['form-i-want']
for i in range(len(my_form.fields.get('field-i-want'))):
if my_form.fields.get('field-i-want')[i].value == "value-to-uncheck":
my_form.fields.get('field-i-want')[i].checked = False
Obviously this is very hacky looking code and there must be a better way. Also, this doesn't actually uncheck the box I want: when I then iterate through the checkboxes in the form there is no longer an element with the value value-i-want: the value has been set to None. And when I submit the form it behaves as if the nothing was done to the form.
Unfortunately your method for setting the checked status of the input will indeed have the unwanted side-effect of the input element being deleted.
As per the docs, to mark a checkbox input as being checked you will want to write:
my_form['field-i-want'] = True
Where 'field-i-want' is the value of the name attribute of the input element.

How to add a line number to each row of a tabularinline block

I have a ModelAdmin class with an inline of type TabularInline. What I would like is for each row of the TabularInline to have a line number displayed to the left of it. This number would increment as new records are added to the inline, and would be displayed when the form is being edited.
I prefer the line number not be a part of the model for the inlined data, but rather be generated each time a new record is added to or displayed by the inline block. I don't need to keep this number in the database. It is for reference only on another field in the ModelAdmin class.
I'm new to django, and I can't seem to figure out how to make this happen.
Any suggestions would be appreciated.
Regards,
Rick
You can number existing inlines easily through the admin class with a class variable and a method to return the line number:
class MyInlineAdmin(admin.TabularInline):
line_numbering = 0
fields = ('line_number', 'other_field')
readonly_fields = ('line_number',)
def line_number(self, obj):
self.line_numbering += 1
return self.line_numbering
line_number.short_description = '#'
This will number any inlines in the order they appear, including any extra (blank) inlines that are included. If you add a single inline via the "Add another" link, it's line number will be correct (incremented by one from the last one), however if you add more than one inline via the link, subsequent ones will still have the same line number as the last one.
Not perfect, but better than nothing.

django admin saving inline with blank value

I have two models as
class Employer(models.Model):
..
..
class JobTitle(models.Model):
type = models.IntegerField(null=False, choices=JobTitles.CHOICES,blank=True,default=0)
employer = models.OneToOneField(Employer,unique=True,null=False)
I have defined admin for Employer and defined JobTitle as inline class.
However, when I saved an Employer object without selecting JobTitle, it raises the error invalid literal for int() with base 10: '' due to the type field of JobTitle.
I would like to set type to 0, as I defined default=0, if I don't select any jobtitle when employer is saved.
How can I achieve this ?
So, I'm inferring this from the code you posted, but I think that you're mixing strategies here. I assume based on the way you've constructed your question that you want to me able to make a job title with no type. Alternatively, you want one job type to be the default selection. I further infer, although now I'm stretching a bit, that you have a custom "No Type" choice (you didn't show us your JobTitles.CHOICES tuple so I have to guess) that corresponds to the 0 value that you've set as the default.
If you want to have a default job type (even if you're calling it "No Job Type" or something similar), then you should set it using the default keyword argument (as you have done) and you should set null=False and blank=False (alternatively, omit both, as these are the default values).
However, if you want it to be possible that there is a job title with no type, then unless you have some reason in your implementation to do something else, the purest way to represent this in data is to use the null SQL value. If you want to go that route, the correct way to do it is to set blank=True and null=True, and then either set default=None or omit that keyword argument entirely.
That should get you the behavior that you seek.
For Model arguments, default is used when Django save your data to Database...
On the other hand, your error occurs during data parsing. When you submit your form, django parses data to relevant type if needed. When your html form is submitted, django recieves string data like
{'title':'12', 'employer_id':'23'...}
So, django have to parse type into ineger before it save it to the database... Since your forms sends a nullstring '' , int('') simply fails with TypeError.
What you must do is removing blank=True. Or somehow you must override the default behaviour of combobox default value and set it 0 instead of ''.