How to override field name attribute in django model form? - python-3.x

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.

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}

Django dynamic form subclass list

I'm trying to understand how can I define model as a class containing 2 integers and 1 charfield and then make it a part of another model.
Example of data I need
I guess in object oriented programming I should define model class like this:
class Component(models.Model):
pipe_type = models.CharField(max_length=200)
length = models.IntegerField()
amount = models.IntegerField()
And then I don't know how can I use it with django models, it should be something like this:
class Item(models.Model):
name = models.CharField()
components_needed = ? LIST OF Component class ?
Also, since components needed size will wary for objects, it should be possible to extend it's size with button on a page, for example there could be 3 input fields and next to them would be "+" and "-" button to add/remove another set of 3 input fields
I spent entire day looking for solution, but at this point I'm not sure if django can handle this. I'm new to python and django, so there are many things I do not understand.
I will be grateful for any kind of help
the only way now( you canot put multi FK in one cell) is like django itself using with user/groups so you need 3 models.
in django there is group, user and user_group so i suggesting for you:
class Component(models.Model):
pipe_type = models.CharField(max_length=200)
length = models.IntegerField()
amount = models.IntegerField()
class Item(models.Model):
name = models.CharField()
class Item_Component(models.Model):
Component = models.ForeignKey(Component, on_delete=models.CASCADE)
Item = models.ForeignKey(Item, on_delete=models.CASCADE)
so now in third model you can have multiple rows with item and with diffrent component.
open yours db viewer app and see django user_group table.

Django Form Field Not Setting Value After Submitting

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.

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.

Yii2 : Getting class name from relation attribute

I went through all API documentation of Yii 2.0 to find a way to reverse back to relation class name from a model attribute.
let us suppose that class Customer has a relation
$this->hasOne(Country::className(), ['id' => 'countryId']);
and in a controller function the parameter was the attribute "countryId". How is it possible to detect the class name for the related model
Get the name of the class by removing Id from the end of the variable and capitalize it. But I cannot image any situation where this would be a normal development practice. You can also define am array to make this translation for the model.
You can try to use http://php.net/manual/en/intro.reflection.php to get the names of all the functions and try to guess the name of the relation / model based on the name of the field. If you name your classes and relation fields in a proper name then you should be able to try to again guess the model.
This still feels like a hack, create a function that returns the name of the model based on the field... easiest solution. I know you try to be lazy but this is a hacky way of programming.
I'm not very clear on what data you have to start with here. If you only have a column countryId I am not sure. But say you have the relation name 'country' and the following code in your Customer model:
public function getCountry()
{
return $this->hasOne(Country::className(), ['id' => 'countryId']);
}
This is what I would do:
$relationName = 'country';
$customer = new Customer;
$relation = $customer->getRelation($relationName);
$relationModelClass = $relation->modelClass;
You could look at \yii\db\ActiveQuery::joinWithRelations() for how they do it.

Resources