Django Generic CreateView - how to populate fields but not display them - django

I want to create hidden fields that are pre or post populated using the generic CreateView but as far as I can see I only have two choices that do not require specifying a form.
This will display the fields prepopulated:
class FootCreate(CreateView):
model = Footprint
fields = ["source","size","notes", "parent", "created_by"]
success_url = reverse_lazy('home')
def get_initial(self):
parent = Object.objects.get(id=self.kwargs['obj_id'])
return { 'parent': parent, 'created_by': self.request.user }
Or remove the fields I don't want to display from the fields list:
fields = ["source","size","notes"]
but now the fields are not populated on the form so it doesn't validate.
I know I can handle this by subclassing the form or using javascript to hide the fields on the form but I wondered if there is an option to have the equivalent of:
fields = ["source","size","notes", "parent", "created_by"]
hidden_fields = ["parent", "created_by"]
within the view?
ANSWER
No - But a custom model form takes only a few lines. Give full list of fields in the view.:
class FootprintForm(ModelForm):
class Meta:
model = Footprint
widgets = {'created_by': forms.HiddenInput, "object": forms.HiddenInput}

As I now there is no way to do what you want in CBV and I think there is no reason to do it in the View. The form logic should be encapsulated in the Form and you can do it easy with ModelForm and few lines of code

Related

Model with more than one ForeignKey, ForeignKey can be more than one for one Model

So to make the above possible I have found out that I have to have ManytoMany Field that is not a problem.
That field is in the form as follows:
class Form(forms.ModelForm):
class Meta:
model = MyModel
fields = ['notes', 'scan']
widgets = {
'scan': forms.CheckboxSelectMultiple(),
}
In the view I have this then:
form = Form(request.POST)
if from.is_valid():
inst = from.save(commit=False)
inst.something = something
inst.save()
Now what do I do, to save the test or scan from the form?
I tried :
inst.test.add(form.cleaned_data['test'])
But that doesn't work for test or scan.
The Model looks like this:
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
notes = models.TextField(default='')
scan = models.ManyToManyField(Scan)
....
Please help I wasn't able find anything in the Internet about this
Thanks!
The documentation of the Form's save method tells it all: If you have a ModelForm that contains the model's ManyToManyField like this:
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['__all__'] # or fields = ['scans'] assuming scans is the M2M field in MyModel
Then you have two ways to save the relationships:
Directly, using form.save()
Calling save_m2m() is only required if you use save(commit=False). When you use a simple save() on a form, all data – including many-to-many data – is saved without the need for any additional method calls.
Or indirectly because you want to manipulate the instance before saving:
if form.is_valid():
instance = form.save(commit=False)
instance.some_field = some_value
instance.save()
form.save_m2m() # this saves the relationships

Django - multiple models in one page

I have a model that looks like this:
models.py
class BHA_List(models.Model):
well = models.ForeignKey(WellInfo, 'CASCADE', related_name='bha_list')
bha_number = models.CharField(max_length=100)
class BHA_Drill_Bit(models.Model):
bha_number = models.ForeignKey(BHA_List, 'CASCADE', related_name='bha_drill_bit')
bit_type = models.CharField(max_length=111)
class BHA_overall(models.Model):
bha_number = models.ForeignKey(BHA_List, 'CASCADE', related_name='bha_overall')
drill_str_name = models.CharField(max_length=111)
class BHA_Motor(models.Model):
bha_number = models.ForeignKey(BHA_List, 'CASCADE', related_name='bha_drill_bit')
motor_type = models.CharField(max_length=111)
BHA_List is a parent model, and the rest are child models related by ForeignKey. The screenshot is the page I want to create
So, I want to generate a base page using one of the instances in model = BHA_List. In this page, I want to edit model instances that are related to BHA_List by ForeignKey relationship.
I currently have a view that looks like this, but its wrong:
class BHA_UpdateView(UpdateView):
model = BHA_List
pk_url_kwarg = 'pk_alt'
form_class = BHA_overall_Form
By setting model = BHA_List, I was able to get one of the instances in BHA_List, and generate url from it. Right now my views correctly return one of the instances in BHA_List: BHA 1
I attempted to edit child models by setting form_class = BHA_overall_Form. But this doesn't do anything, though it displayed form fields on the user side. After editing and clicking Submit button, the changes are not saved in DB. Someone pointed out that this is because my model in UpdateView and form does not match, as I set model = BHA_List, but form_class = BHA_overall_form.
How can I resolve this issue? Someone else also pointed out using multiple views, but I don't really know how to do it, as I'm very new to Django. Any help will be greatly appreciated. Thanks!
Just so that you know. UpdateView can be used if you want to update a single row in one table. When you set model = BHA_LIST you are saying Django. Hey, Django I want to update this model so render me a form with the fields from this table. You can do this by just setting fields attr on the model or use a form like you did which will customize which fields are shown. Now the good thing about allowing to set our own form is. Though we create a modelForm we can also add extra fields inside it. Now your BHAOverallForm should look like this to accommodate all the fields you need.
forms.py
class BHAOverallForm(forms.ModelForm):
well = models.ForeignKey(WellInfo, 'CASCADE', related_name='bha_list')
bha_number = models.CharField(max_length=100)
bit_type = models.CharField(max_length=111
drill_str_name = models.CharField(max_length=111)
motor_type = models.CharField(max_length=111)
class Meta:
model = BHAList
you can use this form inside your form like you do now. You can also add clean_field to add validations. Now coming to the update part. your views should look like this
views.py
class BHAUpdateView(UpdateView):
model = BHAList
form_class = BHAOverallForm
def form_valid(self, form):
super(BHAUpdateView, self).form_valid(form) # save BHAList to the DB
bha_list = form.instance
bha_drill_bit = bha_list.bhadrillbit_set.first() # assuming you have only one drill_bit per list, if you need more modify your question accordingly.
bha_drill_bit.bit_type = form.cleaned_data.get("bit_type)
bha_drill_bit.save()
# you can do the same for other models as well.

How to show objects in the form field (Multiple Select Field) based on a condition in django form.

I have a many to many field linked with my model1. Now, I created a form for this model1 and added this many to many field as a form field and used FilteredSelectMultiple widget to edit this. Now, the problem is the related many to many field has a soft delete option, which I am tracking with active field in the model2. So, now in the form all the objects are displayed even if they are deleted, is there any way I can show the objects which have active as true in this form field.
My model form looks as follows:
class Editform(form.ModelForm):
class Media:
css = ..
js = ..
class Meta:
Model = model1
fields = [ "x", "y", "ManytoManyfield"]
widgets = {
'ManytoManyfield': FilteredSelectMultiple("Displaay name", False)
}
This answer is close to what you want. I think this may work.
You create an extra field in your ModelForm, populating it with a query.
class Editform(form.ModelForm):
many_to_many_field_active = forms.ChoiceField(choices=[(m2m.id, m2m.name) for m2m in Model2.objects.filter(active=True)])
class Meta:
#...
widgets = {
'many_to_many_field_active': Select(attrs={'class': 'select'}),
I solved this using the multiplechoicefield in my model form as follows.
def __init__(self, *args, **kwargs):
many_to_m_initial = kwargs['instance'].model2.all()
choices = [(m2m.id, m2m.name) for m2m in Model2.objects.filter(active=True)]
self.fields['my_field'] = forms.MultipleChoiceField(choices = choices, widget=FilteredSelectMultiple("verbose name", is_stacked=False, choices=choices))
self.initial['my_field'] = [ m2m.pk fpr m2m in many_to_m_initial ]

Change fields of ModelForm dynamically

Is it possible to change what fields are displayed in a ModelForm, dynamically?
I am trying to show only a small number of fields in a ModelForm when the user adds a new instance (of the Model) from the frontend (using an add form) but larger number of fields when the user edits an instance (using an edit form).
The Form class looks something like this:
class SchoolForm(ModelForm):
class Meta:
model = School
#want to change the fields below dynamically depending on whether its an edit form or add form on the frontend
fields = ['name', 'area', 'capacity', 'num_of_teachers']
widgets = {
'area': CheckboxSelectMultiple
}
labels = {
'name': "Name of the School",
'num_of_teachers': "Total number of teachers",
}
Trying to avoid having two separate classes for add and edit since that doesnt seem DRYish. I found some SO posts with the same question for the admin page where we could override get_form() function but that does not apply here.
Also, this answer suggests using different classes as the normal way and using dynamic forms as an alternative. Perhaps dynamics forms is the way forward here but not entirely sure (I also have overridden __init__() and save() methods on the SchoolForm class).
I'm not suere if is a correct way, but i use some method in class to add fields or delete-it. I used like this:
class someForm(forms.ModelForm):
class Meta:
model = Foo
exclude = {"fieldn0","fieldn1"}
def __init__(self, *args, **kwargs):
super(someForm, self).__init__(*args, **kwargs)
self.fields['foofield1'].widget.attrs.update({'class': 'form-control'})
if self.instance.yourMethod() == "FooReturn":
self.fields['city'].widget.attrs.update({'class': 'form-control'})
else:
if 'city' in self.fields: del self.fields['city']
Hope it helps.

How to render a text box instead of a select box when using model form set

When I render my formset, one of the field renders as a select box because it is a foreign field in the model. Is there a way to change this to a text input? I want to populate that field by using Ajax auto complete. Adding a widget to the modelform is not working because the modelformset_factory takes a model and not a model form.
EDIT
My Model Form
class RecipeIngredientForm(ModelForm):
class Meta:
model = RecipeIngredient
widgets = { 'ingredient' : TextInput(), }
I use it in my view
RecipeIngredientFormSet = modelformset_factory(RecipeIngredient, form=RecipeIngredientForm)
objRecipeIngredients = RecipeIngredientFormSet()
EDITED MODEL FORM
class RecipeIngredientForm(ModelForm):
ingredient2 = TextInput()
class Meta:
model = RecipeIngredient
I create the form set like this
RecipeIngredientFormSet = modelformset_factory(RecipeIngredient, form=RecipeIngredientForm)
objRecipeIngredients = RecipeIngredientFormSet()
QUESTION
Do I have to use the formset in html? Can I just hard code the fields that get generated and using javascript I can create new fields and increment the "form-TOTAL-FORMS"? If I can then I do not have to worry about my model form.
Thanks
modelformset_factory does take a form. Here's the function signature from django.forms.models:
def modelformset_factory(
model, form=ModelForm, formfield_callback=lambda f: f.formfield(),
formset=BaseModelFormSet,
extra=1, can_delete=False, can_order=False,
max_num=0, fields=None, exclude=None):
If this isn't working for you, show some code and I'll try and see what is going wrong.
Edit after various comments As you point out, the widget argument is buggy when used in this way. So the solution is not to use it - it's a very recent addition in any case. Instead, define the field directly on the form:
class RecipeIngredientForm(forms.ModelForm):
ingredient = forms.ModelChoiceField(widget=forms.TextInput))
class Meta:
model = RecipeIngredient