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.
Related
I am trying to solve one issue about saving data in db.
This is an example how I think of it:
class MyModel(models.Model):
id = models.AutoField(primary_key=True)
fieldX = models.SomeFieldType()
#property:
def foo(self):
return self._foo
#foo.setter
def foo(self, var):
self._foo=var
class MyModelForm(models.Modelform):
class Meta:
model = models.MyModel
fields = '__all__'
The thing is I have dict that I am passing to this form (so I am not using view or any provided interaction with user directly. In dict I have some fields and what I want to do is one field that is passed to use it as model property but I do not want it to be saved in db.
So I tried something like:
form = MyModelForm(data_dict)
if form.is_valid():
form.foo = data_dict['data_for_property_not_db']
form.save()
Form does not know that my model has this property.
So basiclly what I want is to write some parts of my data_dict normaly to form and db as always ->works fine
and then I want some data_info pass to that property and use it somewhere in save() as needed without saving it to db itself.
Can you help me with this?
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
I have a Django model with a ManyToManyField.
1) When adding a new instance of this model via admin view, I would like to not see the M2M field at all.
2) When editing an existing instance I would like to be able to select multiple options for the M2M field, but display only a subset of the M2M options, depending on another field in the model. Because of the dependence on another field's actual value, I can't just use formfield_for_manytomany
I can do both of the things using a custom ModelForm, but I can't reliably tell whether that form is being used to edit an existing model instance, or if it's being used to create a new instance. Even MyModel.objects.filter(pk=self.instance.pk).exists() in the custom ModelForm doesn't cut it. How can I accomplish this, or just tell whether the form is being displayed in an "add" or an "edit" context?
EDIT: my relevant code is as follows:
models.py
class LimitedClassForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(LimitedClassForm, self).__init__(*args, **kwargs)
if not self.instance._adding:
# Edit form
clas = self.instance
sheets_in_course = Sheet.objects.filter(course__pk=clas.course.pk)
self.Meta.exclude = ['course']
widget = self.fields['active_sheets'].widget
sheet_choices = []
for sheet in sheets_in_course:
sheet_choices.append((sheet.id, sheet.name))
widget.choices = sheet_choices
else:
# Add form
self.Meta.exclude = ['active_sheets']
class Meta:
exclude = []
admin.py
class ClassAdmin(admin.ModelAdmin):
formfield_overrides = {models.ManyToManyField: {
'widget': CheckboxSelectMultiple}, }
form = LimitedClassForm
admin.site.register(Class, ClassAdmin)
models.py
class Course(models.Model):
name = models.CharField(max_length=255)
class Sheet(models.Model):
name = models.CharField(max_length=255)
course = models.ForeignKey(Course)
file = models.FileField(upload_to=getSheetLocation)
class Class(models.model):
name = models.CharField(max_length=255)
course = models.ForeignKey(Course)
active_sheets = models.ManyToManyField(Sheet)
You can see that both Sheets and Classes have course fields. You shouldn't be able to put a sheet into active_sheets if the sheet's course doesn't match the class's course.
I have different models in my app, the main model having multiple instances of some other models.
models.py:
class Person(models.Model):
...
class Pet(models.Model):
owner = models.ForeignKey(Person)
...
forms.py:
class PersonForm(forms.ModelForm):
class Meta:
model = Person
PetFormSet = inlineformset_factory(Person, Pet, extra = 1)
views.py:
def add_template(request):
person_form = PersonForm(prefix = 'person_form')
pet_form = PetFormSet(instance = Person(), prefix = 'pet_form')
... # check is_valid(), render when no POST data is present, etc.
The point is the addition works perfectly, storing each instance in the corresponding database table, etc. I use jquery.formset-1.2.js to manage the dynamical addition-deletion of forms in the "add.html".
But I later want to edit the stored info through a view, i.e., load the data from the object I pass in the request, and render the formsets with the data obtained from the database (if there are 3 pets related to the person being editted, display 3 form instances of "Pet" with their value displayed).
I would like to also add new Pets and remove existing ones, as well as changing existing field values.
I have tried with querysets in the FormSet creation, but it doesn't display anything.
Any idea how to ease this issue? Maybe using an app for an easier formset management?
Thank you
I'm not sure if I understand your problem correctly. If you want to display all Pets that belong to the Person:
def show_pets(request, person_id=None):
person = get_object_or_404(Person.objects.select_related(), id=person_id)
if request.method == 'POST':
person_form = PersonForm(request.POST, instance=person)
pet_formset = PetFormSet(request.POST, instance=person)
# Rest of your code here
else:
person_form = PersonForm(instance=person)
pet_formset = PetFormSet(instance=person)
return render_to_response('your_template.html', {'person_form': person_form,
'pet_formset': pet_formset)})
Then you just need to render both forms in your template and add add and delete functionality
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