Can someone show the example of usage of two model forms in one view/template? The task is to process more than one form in one CBV, where model of the second form has FK to first form model, so value from select widget from first form must be processed as value for object, created in second form.
My forms looks like this:
class CompanyEditForm(ModelForm):
name = CharField(label="Наименование", required=True)
type = ModelChoiceField(queryset=CompanyTypes.objects.all(), label="Тип компании", empty_label=None, initial=3)
description = CharField(label="Описание компании", required=False, widget=forms.Textarea(attrs={'cols': 40, 'rows':3}))
www = CharField(label="WEB сайт", required=False)
class Meta:
model = Companies
fields = ('type', 'name', 'description', 'www')
class BranchEditForm(ModelForm):
name = CharField(label="Наименование офиса", required=True)
type = ModelChoiceField(queryset=BranchTypes.objects.all(), label="Тип отделения", empty_label=None, initial=1)
class Meta:
model = Branches
exclude = ('company', 'address')
class AddressEditForm(ModelForm):
postalcode = IntegerField(label="Почтовый код", required=False)
city = CharField(label="Город", required=True)
street = CharField(label="Улица", required=True)
app = CharField(label="Дом", required=True)
app_extra = CharField(label="Корпус / Строение", required=False)
comment = CharField(label="Примечание к адресу", required=False)
exclude = ('company',)
class Meta:
model = Addresses
fields = ('postalcode', 'city', 'street', 'app', 'app_extra', 'comment')
UPDATE
I wrote this mixin:
class MultiFormCreate(FormMixin, TemplateResponseMixin, View):
formconf = None
def get_form_classes(self):
form_classes = {}
for key, params in self.formconf.items():
form_classes[key] = params.formclass
return self.form_classes
def get_initial(self, classname):
inicial = {}
if 'inicial' in self.formconf[classname]:
inicial = self.formconf[classname]['inicial'].copy()
return inicial
def get_form_kwargs(self, classname):
kwargs = {'initial': self.get_initial(classname), 'prefix': classname}
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs
def get_forms(self):
for classname, params in self.formconf.items():
log.info("Name: %s, Params: %s" % (classname, params))
return dict(
[(classname, params['formclass'](**self.get_form_kwargs(classname))) for classname, params in self.formconf.items()])
def get(self, request, *args, **kwargs):
forms = self.get_forms()
return self.render_to_response(self.get_context_data(forms=forms))
def get_success_url(self):
if self.success_url:
url = force_text(self.success_url)
else:
raise ImproperlyConfigured(
"No URL to redirect to. Provide a success_url.")
return url
then in a view i only need to write processing in post:
class CompanyCreate(MultiFormCreate):
template_name = 'company/edit.html'
success_url = '/forbidden/'
formconf = {
'company': {'formclass': CompanyEditForm, 'inicial': {'name': "TESTNAME"}},
'branch': {'formclass': BranchEditForm},
'address': {'formclass': AddressEditForm}
}
def post(self, request, *args, **kwargs):
forms = self.get_forms()
cform = forms['company']
aform = forms['address']
bform = forms['branch']
if cform.is_valid() and aform.is_valid() and bform.is_valid():
''' Creating main form form object (by saving tthe form) '''
company_object = cform.save()
''' Creating dependant object '''
address_object = aform.save(commit=False)
branch_object = bform.save(commit=False)
''' assigning dependent fields '''
address_object.company = company_object
''' saving dependent _object_ '''
address_object.save()
''' managing another dependent fields '''
branch_object.company = company_object
branch_object.address = address_object
''' saving last object '''
branch_object.save()
return HttpResponseRedirect(self.get_success_url())
else:
forms = self.get_forms()
return self.render_to_response(self.get_context_data(forms=forms))
Code for view file :
if request.method == "POST":
company_form = CompanyEditForm(request.POST)
if company_form.is_valid():
company_object = company_form.save()
post_data = request.POST.copy()
branch_form = BranchEditForm(post_data)
branch_form.data['company'] = company_object
if branch_form.is_valid():
branch_object.save()
rest implement your business logic ..
Related
So I have this thing.
These are the relationships of my models
To define an event you have a Place, a QR Link and an ImageAlbum which have up to five Images.
An event may or may not have a Place
An event MUST have the main image which is marked by the main attr in the Image Form.
All the forms are in the same page, they all are processed with a single Submit
Now my troubles begin and end in the Forms Department, i have several Model Forms:
#FORMS FILE
class EventForm(ModelForm):
date_time_start = forms.DateTimeField(input_formats=["%Y-%m-%d"], required=True, widget=forms.DateTimeInput(format="%Y-%m-%d", attrs={"id": "cal_start", "placeholder": "yyyy-mm-dd"}))
has_place = forms.BooleanField()
class Meta:
model = Event
fields = ['title', 'subtitle', 'phrase', 'date_time_start', 'prices', 'has_place']
class BaseImageFormSet(BaseModelFormSet):
def clean_main(self):
"""Checks that there is only one main image"""
if any(self.errors):
return
boolean_arr = []
for form in self.forms:
if self.can_delete and self._should_delete_form(form):
continue
main_data = form.cleaned_data.get('main')
main = main_data if main_data is not None else False
if sum(boolean_arr) > 1:
raise ValidationError('There can only be one main image.')
boolean_arr.append(main)
class PlaceForm(ModelForm):
class Meta:
model = EventPlace
exclude = ['uuid', 'event']
class QRForm(ModelForm):
class Meta:
model = EventQR
exclude = ['uuid', 'event']
My headache is with the Images, I have to make a ModelFormSet of Images but I have to create first the album which connects the images to the event.
Here are my views:
#VIEWS FILE
class NewEventView(BaseView):
def get(self, request, *args):
ImageFormSet = modelformset_factory(
EventImage, fields=('image', 'main'),
extra=5, max_num=5, formset= BaseImageFormSet)
image_formset = ImageFormSet(prefix='images')
event_form = EventForm(prefix='event')
place_form = PlaceForm(prefix='place')
qr_form = QRForm(prefix='qr')
context = {'forms': {'Evento': event_form, 'Lugar': place_form,
'QR Links': qr_form}, 'image_formset': image_formset, }
return render(request, "events/events_add.html", context)
def post(self, request):
event_form = EventForm(request.POST, prefix='event')
if event_form.is_valid():
new_event = event_form.save(commit=False)
if event_form.cleaned_data['has_place']:
place_form = PlaceForm(request.POST, prefix='place')
if place_form.is_valid():
qr_form = QRForm(request.POST, prefix='qr')
if qr_form.is_valid():
new_album = EventAlbum(event=new_event)
ImageFormSet = modelformset_factory(
EventImage, fields=('image', 'main'),
extra=5, max_num=5, formset= BaseImageFormSet)
image_formset = ImageFormSet(request.POST, request.FILES,
prefix='images')
if image_formset.is_valid():
event_form.save()
new_album.save()
new_place = place_form.save(commit=False)
new_place.event = new_event
new_qr = qr_form.save(commit=False)
new_qr.event = new_event
new_place.save()
new_qr.save()
image_formset.save()
return HttpResponse('Ok', content_type='text')
else:
return Response(image_formset.errors, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(qr_form.errors, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(place_form.errors, status=status.HTTP_400_BAD_REQUEST)
else:
qr_form = QRForm(request.POST, prefix='qr')
if qr_form.is_valid():
new_album = EventAlbum(event=new_event)
ImageFormSet = modelformset_factory(
EventImage, extra=5, max_num=5, formset=BaseImageFormSet)
image_formset = ImageFormSet(
request.POST, request.FILES, prefix='images')
if image_formset.is_valid():
event_form.save()
new_album.save()
image_formset.save()
qr_form.save()
return HttpResponse('Ok', content_type='text')
else:
return Response(image_formset.errors, status=status.HTTP_400_BAD_REQUEST)
else:
return Response(qr_form.errors, status=status.HTTP_400_BAD_REQUEST)
return Response(event_form.errors, status=status.HTTP_400_BAD_REQUEST)
How would you do about saving the images?
And the whole event with its dependencies.
Please help i am stuck by now
I have a form like this,
class UniqueUrlForm(forms.ModelForm):
cc_number = cc_form.CardNumberField(label='Card Number')
cc_expiry = cc_form.CardExpiryField(label='Expiration Date')
cc_code = cc_form.SecurityCodeField(label='CVV/CVC')
class Meta:
model = Transactions
fields = ['customer_name', 'customer_phone', 'customer_email', 'total_amount', 'cc_number', 'cc_expiry',
'cc_code']
def __init__(self, *args, **kwargs):
super().__init__()
store_id = kwargs.get("store_id", "1")
payment_page = get_object_or_404(
PaymentPageDisplayDetails.objects.filter(store_id=store_id).values("payment_fields_visible"))
with urllib.request.urlopen(payment_page['payment_fields_visible']) as url:
display_fields = json.loads(url.read().decode())
for field_name in display_fields:
self.fields[field_name] = forms.CharField(required=False)
and a view like this,
def getpaymentpage(request, store_identifier):
uniqueurl_form = UniqueUrlForm(request.POST or None, request.FILES or None, {"store_id": 1})
if uniqueurl_form.is_valid():
trx_details = {
"amount": uniqueurl_form.cleaned_data['amount'],
"customer_email": uniqueurl_form.cleaned_data['customer_email'],
"customer_phone": uniqueurl_form.cleaned_data['customer_phone'],
"cc_number": uniqueurl_form.cleaned_data['cc_number'],
"cc_name": uniqueurl_form.cleaned_data['customer_name'],
"cc_month": uniqueurl_form.cleaned_data['cc_month'],
"cc_year": uniqueurl_form.cleaned_data['cc_year'],
"cvv": uniqueurl_form.cleaned_data['cvv'],
}
return HttpResponse(trx_details)
context = {
'form': {
uniqueurl_form,
},
"page": store_display,
}
return render(request, 'unique_url.html', context)
I have tried print(uniqueurl_form.errors) it always returns empty and uniqueurl_form.is_valid() as false.
Is it because I'm adding dynamic fields to the form.
I have referred the following,
dynamically add field to a form
What am I doing wrong here?
Thank you for your suggestions.
weirdly it started working when i made following changes,
class UniqueUrlForm(forms.ModelForm):
cc_number = cc_form.CardNumberField(label='Card Number')
cc_expiry = cc_form.CardExpiryField(label='Expiration Date')
cc_code = cc_form.SecurityCodeField(label='CVV/CVC')
class Meta:
model = Transactions
fields = ['customer_name', 'customer_phone', 'customer_email', 'total_amount', 'cc_number', 'cc_expiry',
'cc_code']
def __init__(self, *args, **kwargs):
store_id = kwargs.get("store_id", "1")
super(UniqueUrlForm, self).__init__(*args, **kwargs)
payment_page = get_object_or_404(
PaymentPageDisplayDetails.objects.filter(store_id=store_id).values("payment_fields_visible"))
with urllib.request.urlopen(payment_page['payment_fields_visible']) as url:
display_fields = json.loads(url.read().decode())
for field_name in display_fields:
self.fields[field_name] = forms.CharField()
my guess is because I did not specify class name in my .super() event though it was appending the fields it was not sure what validations to put on those fields.
I have one model Measurement, two forms MeassurementSystolicPressureForm and MeassurementDiastolicPressureForm. I want to make a view that allows user to add both of them to the database. Each has fields: username, measurement_date, value, measurement_type. When I fill forms on my webpage two records are added to the db, each has a good username and measurement_type, but measurement_date and value are the same for both records. Can you help me spotting what I'm doing wrong?
Here is my code:
models.py
class Measurement(models.Model):
value = models.IntegerField()
measurement_type = models.CharField(max_length=6, default='measurement', blank=True)
username = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
measurement_date = models.DateTimeField(default=datetime.now, editable=True)
forms.py
class MeassurementSystolicPressureForm(ModelForm):
class Meta:
model = Measurement
fields = ['value', 'measurement_date']
class MeassurementDiastolicPressureForm(ModelForm):
class Meta:
model = Measurement
fields = ['value', 'measurement_date']
views.py
def new_measurement(request):
if request.method == 'POST':
form_SP = MeassurementSystolicPressureForm(request.POST or None)
form_DP = MeassurementDiastolicPressureForm(request.POST or None)
if form_CS.is_valid() or form_CR.is_valid():
temp_S = form_SP.save(commit=False)
temp_S.username = request.user
temp_S.measurement_type = 'syspres'
temp_S.save()
temp_D = form_DP.save(commit=False)
temp_D.username = request.user
temp_D.measurement_type = 'diapres'
temp_D.save()
return redirect('/')
else:
form_SP = MeassurementSystolicPressureForm()
form_DP = MeassurementDiastolicPressureForm()
args = {'form_SP': form_SP, 'form_DP': form_DP}
return render(request, 'measurements.html', args)
If for example I submit data for:
Systolic Pressure:
value: 120
date: 2019-01-15 16:15:32
Diastolic Pressure:
value: 80
date: 2019-01-15 15:00:00`
In my database I have two records:
username: Julka, measurement_type:
syspres, value: 80, date: 2019-01-15 15:00:00
username: Julka, measurement_type: diapres, value: 80, date: 2019-01-15 15:00:00
I have no idea what to do.
In an HttpRequest object, the GET and POST attributes are instances of django.http.QueryDict. This type alone cannot determine which form was submitted. Your forms have the same fields, so then one form is valid, other form valid too. That's why you have measurement_date and value are the same for both records. To solve this problem, you can add additional hidden fields to your forms and look at them from which form was sent. Some like this:
class MeassurementSystolicPressureForm(ModelForm):
flag_Systolic = forms.IntegerField()
class Meta:
model = Measurement
fields = ['value', 'measurement_date']
def __init__(self, *args, **kwargs):
super(MeassurementSystolicPressureForm, self).__init__(*args, **kwargs)
self.fields['flag_Systolic'].widget = forms.HiddenInput()
class MeassurementDiastolicPressureForm(ModelForm):
flag_Diastolic = forms.IntegerField()
class Meta:
model = Measurement
fields = ['value', 'measurement_date']
def __init__(self, *args, **kwargs):
super(MeassurementDiastolicPressureForm, self).__init__(*args, **kwargs)
self.fields['flag_Diastolic'].widget = forms.HiddenInput()
and in your views:
def new_measurement(request):
if request.method == 'POST':
if 'flag_Systolic' in request.POST:
form_SP = MeassurementSystolicPressureForm(request.POST)
if form_SP.is_valid():
temp_S = form_SP.save(commit=False)
temp_S.username = request.user
temp_S.measurement_type = 'syspres'
temp_S.save()
return redirect('/')
elif 'flag_Diastolic' in request.POST:
form_DP = MeassurementDiastolicPressureForm(request.POST)
if form_DP.is_valid():
temp_D = form_DP.save(commit=False)
temp_D.username = request.user
temp_D.measurement_type = 'diapres'
temp_D.save()
return redirect('/')
else:
form_SP = MeassurementSystolicPressureForm()
form_DP = MeassurementDiastolicPressureForm()
args = {'form_SP': form_SP, 'form_DP': form_DP}
return render(request, 'measurements.html', args)
I know maybe it is too late but it might be helpful for other people facing the same problem.
One easier solution would be creating the object in the View and passing it to both forms:
from .models import Measurement
def new_measurement(request):
user=request.user #the authenticated user
if request.method == 'POST':
measurement=Measurement(username=user)
form_SP = MeassurementSystolicPressureForm(request.POST or None, instance=measurement)
form_DP = MeassurementDiastolicPressureForm(request.POST or None, instance=measurement)
if form_CS.is_valid() or form_CR.is_valid():
form_CS.save()
form_CR.save()
return redirect('/')
else:
form_SP = MeassurementSystolicPressureForm()
form_DP = MeassurementDiastolicPressureForm()
args = {'form_SP': form_SP, 'form_DP': form_DP}
return render(request, 'measurements.html', args)
Here is models.py:
class User(TimeStampedModel):
id = models.AutoField(primary_key=True)
class Shoe(models.Model):
size = models.IntegerField(help_text="The shoe\'s size", choices=sizeChoices)
user = models.ForeignKey('User', related_name='shoes')
brand = models.ForeignKey('Brand', related_name='shoes')
class Brand(models.Model):
name = models.CharField(max_length=64, choices = brandChoices)
class ShoeForm(ModelForm):
def __init__(self, *args, **kwargs):
super(ShoeForm, self).__init__(*args, **kwargs)
self.fields['brand'].required = True
self.fields['size'].required = True
class Meta:
model = Shoe
fields = ['brand', 'size']
exclude = ['user']
views.py:
def index(request):
ShoeFormSet = modelformset_factory(Shoe, form=ShoeForm, extra=3)
NewShoeFormSet = modelformset_factory(Shoe, form=NewShoeForm)
if request.method == 'POST':
shoe_formset = ShoeFormSet(request.POST, request.FILES, prefix='shoes')
new_shoe_formset = NewShoeFormSet(request.POST, request.FILES, prefix='new_shoes')
if shoe_formset.is_valid() and new_shoe_formset.is_valid():
#do something with the cleaned data
else:
shoe_formset = ShoeFormSet(prefix='shoes')
new_shoe_formset = NewShoeFormSet(prefix='new_shoes')
return render(request, 'shoes/input_shoes.html', {
'shoe_formset': shoe_formset,
'new_shoe_formset': new_shoe_formset,
})
But I get this error: DatabaseError: no such column: shoes_shoe.user_id, which occurs on the line shoe_formset = ShoeFormSet(prefix='shoes') when the form renders. What's going wrong?
I have the following view
class ProductUpdateView(BaseProductUpdateView):
form_class = ProductForm
slug_field = "id"
def form_valid(self, form):
form.instance.hotel = form.cleaned_data["hotel"]
form.instance.parent = form.cleaned_data["parent"]
return super(ProductUpdateView, self).form_valid(form)
and following form:
class ProductForm(BaseProductForm):
hotel = forms.ModelChoiceField(queryset=Hotel.objects.all(), widget=forms.TextInput)
parent = forms.ModelChoiceField(queryset=Product.objects.all(), widget=forms.TextInput, required=False)
class Meta:
model = Product
exclude = ('slug', 'parent', 'hotel', 'status', 'score', 'product_class',
'recommended_products', 'related_products',
'product_options', 'attributes', 'categories')
When I save the form, form saved successfully. I can see the saved values of hotel and parent in admin, but when I re-open the update form page, other fields returns but hotel and parent fields return blank. Any ideas?
I handled it like below
views.py
class ProductUpdateView(BaseProductUpdateView):
form_class = ProductForm
slug_field = "id"
def get_form_kwargs(self, **kwargs):
kwargs = super(ProductUpdateView, self).get_form_kwargs(**kwargs)
kwargs['initial']['hotel'] = self.get_object().hotel.id if \
self.get_object().hotel else None
kwargs['initial']['parent'] = self.get_object().parent.id if \
self.get_object().parent else None
return kwargs
def form_valid(self, form):
form.instance.hotel = form.cleaned_data["hotel"]
form.instance.parent = form.cleaned_data["parent"]
return super(ProductUpdateView, self).form_valid(form)
forms.py
class AutoCompleteModelChoiceField(forms.ModelChoiceField):
widget = forms.TextInput
def clean(self, value):
value = super(AutoCompleteModelChoiceField, self).clean(value)
return value
class ProductForm(BaseProductForm):
hotel = AutoCompleteModelChoiceField(queryset=Hotel.objects.all())
parent = AutoCompleteModelChoiceField(queryset=Product.objects.all(),
required=False)