ModelForm not passing all parameters - django-views

After some great help by #kreigar I was able to start using django's ModelForm instead of form.Forms. Only problem now is that I am not passing the two primary keys to associate my review form :-(.
Currently my view looks like such:
#views.py
#login_required
def wine_review_page(request, wine_id):
wine = get_object_or_404(Wine, pk=wine_id)
review = None
if Review.objects.filter(user=request.user, wine=wine).exists():
review = Review.objects.get(user=request.user, wine=wine)
if request.method == 'POST':
form = WineReviewForm(request.POST, instance=review)
if form.is_valid():
form.save()
return HttpResponseRedirect('/detail/%s/' % wine_id )
else:
form = WineReviewForm(instance=review)
variables = RequestContext(request, {'form': form, 'wine': wine })
return render_to_response('wine_review_page.html', variables)`
and my model form like this:
#models.py
class WineReviewForm(ModelForm):
class Meta:
model = Review
widgets = {
'wine': HiddenInput(),
'user': HiddenInput(),
'like_dislike': HiddenInput(),
'fave': HiddenInput(),
'sweet_dry': HiddenInput(),
'crisp_buttery: HiddenInput(),
'fruity_earthy': HiddenInput(),
'smooth_spicy': HiddenInput(),
'light_robust': HiddenInput(),
'label_rating': HiddenInput()
}
Wine_review_form:
<form method="post" action="/review/{{ wine.id }}/">
{% csrf_token %}
{{form.as_p}}
<div id="form_div" data-role="fieldcontain">
<script>
$('#form_div > input').hide();
</script>
{% if wine.wine_kind == 'whites' %}
<div class="slider_labels">
<span id="sweet_text"class="left_slider_text">Sweet</span>
<span id="dry_text"class="right_slider_text">Dry</span>
</div>
<input type="range" name="sweet_dry" id="sweet_dry_slider" value="50" min="0" max="100" onchange="sweetDryValue(this.value)" />
<div class="slider_labels">
<span id="crisp_text"class="left_slider_text">Crisp</span>
<span id="buttery_text"class="right_slider_text">buttery</span>
</div>
<input type="range" name="crisp_buttery" id="crisp_buttery_slider" value="50" min="0" max="100" onchange="crispButteryValue(this.value)" />
{% else %}
<div class="slider_labels">
<span id="fruity_text"class="left_slider_text">Fruity</span>
<span id="earthy_text"class="right_slider_text">Earthy</span>
</div>
<input type="range" name="fruity_earthy" id="fruity_earthy_slider" value="50" min="0" max="100" onchange="fruityEarthyValue(this.value)" />
<div class="slider_labels">
<span id="smooth_text" class="left_slider_text">Smooth</span>
<span id="spicy_text" class="right_slider_text">Spicy</span>
</div>
<input type="range" name="smooth_spicy" id="smooth_spicy_slider" value="50" min="0" max="100" onchange="smoothSpicyValue(this.value)" />
{% endif %}
<div class="slider_labels">
<span id="light_text"class="left_slider_text">Light</span>
<span id="robust_text" class="right_slider_text">Robust</span>
</div>
<input type="range" name="light_robust" id="light_robust_slider" value="50" min="0" max="100" onchange="lightRobustValue(this.value)" />
<div class="slider_labels">
<span id="sad" class="left_slider_text">Sad</span>
<span id="rad" class="right_slider_text">Rad</span>
<div id="label_rating">Label Rating</div>
</div>
<input type="range" name="label_rating" id="label_rating_slider" value="50" min="0" max="100" onchange="labelRatingValue(this.value)" />
<br>
<br>
<div class="ui-grid-b">
<div class="ui-block-a">
<input type="radio" name="like_dislike" id="like" value="like" />
<label for="like">like</label>
</div>
<div class="ui-block-b">
<input type="radio" name="like_dislike" id="dislike" value="dislike" />
<label for="dislike">dislike</label>
</div>
<div class="ui-block-c">
<input type="checkbox" name="fave" id="checkbox-1" class="custom" />
<label for="checkbox-1">fave</label>
</div>
</div>
</fieldset>
</div>
<input type="submit" value="Judged!" rel="external"/>
When checking my post request the only things I am not passing are the: wine_id and the user_id. This is really confusing.
Am I missing soomething simple?
I looked through documentation and examples but no luck so far.

It actually looks like you wouldn't need your wine_id and user_id (unless you want to allow users to post reviews for other users, or switch wines they are reviewing on the fly).
This should work for you:
#login_required
def wine_review_page(request, wine_id):
wine = get_object_or_404(Wine, pk=wine_id)
if Review.objects.filter(user=request.user, wine=wine).exists():
review = Review.objects.get(user=request.user, wine=wine)
else:
review = Review(user=request.user, wine=wine) #this added
if request.method == 'POST':
form = WineReviewForm(request.POST, instance=review)
if form.is_valid():
form.save()
return HttpResponseRedirect('/detail/%s/' % wine_id )
else:
form = WineReviewForm(instance=review)
variables = RequestContext(request, {'form': form, 'wine': wine })
return render_to_response('wine_review_page.html', variables)
If you do want to allow the user to modify either of those fields, edit your question to show your template or how the users are submitting data, since all your fields are using hiddenInput widgets, Its hard to tell how any data would get into the request.POST

Related

Django form is saved but result field is empty in database

Django form is saved but "result" field is showing empty in database.
Even after populating the filed from admin panel, it is saved but it still shows empty.
Models.py
class Result(models.Model):
class Choises(models.TextChoices):
POSITIVE = "POSITIVE", "Positive"
NEGATIVE = "NEGATIVE", "Negative"
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=50, default=None)
result = models.CharField(max_length = 100, choices=Choises.choices, blank=False
)
resultDoc = models.ImageField(upload_to='testResults', height_field=None, width_field=None,)
def __str__(self):
return self.user.username
Forms.py
class resultForm(forms.ModelForm):
class Meta:
model = Result
fields = ['name', 'result', 'resultDoc']
views.py
def inputResult(request, pk):
user = User.objects.filter(id=pk).first()
profile = newProfile.objects.filter(user=user).first()
if profile == None:
profile = oldProfile.objects.filter(user=user).first()
rForm = resultForm(request.POST, request.FILES)
if request.method == 'POST':
rForm = resultForm(request.POST, request.FILES)
if rForm.is_valid():
order = rForm.save(commit=False)
order.user_id = pk
order.save()
return redirect('stored_records')
else:
rForm = resultForm()
context = {'user' : user, 'profile':profile, 'rForm': rForm}
return render(request, 'Testing booth End/input-result-customer-info.html', context)
input-result-customer-info.html
<form action="" method = "POST" enctype= "multipart/form-data">
{% csrf_token %}
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" placeholder="Uploaded By/Doctor Name">
</div>
<div class="mb-3">
<label for="result" class="form-label">Result</label>
<select class="form-select" id="result" name="result" class="form-control">
<option value="POSITIVE">Positive</option>
<option value="NEGATIVE">Negative</option>
</select>
</div>
<div class="mb-3">
<label>Upload Scan File</label>
<div class="upload d-flex justify-content-between">
<div class="file-placeholder">Upload Scan File</div>
<input type="file" class="form-control d-none" id="resultDoc" name="resultDoc" >
<label for="resultDoc" class="form-label cam-img"> <img src="{% static 'user/images/Camera.png' %}"> </label>
</div>
</div>
<button class="btn btn-primary w-50 ms-auto d-block h-100" type="submit">Upload</button>
</form>
enter image description here
I think that the reason this doesnt work is that you created a form (rForm) in the backend but then you don't use it in the frontend.
This is how you should render your form in the the frontend:
<form method="post">
{{ rForm.as_p }} # This is the easiest possible implementation
<button type="submit">Submit</button>
</form>
If you want to take control of how the form is rendered, then you have to make sure that the input fields are named in the way that your backend expects. You can do it entirely manually or semi-manually, but your field names have to be set correctly or nothing will work.
Example of typical approach, say in case you have several similar text inputs
{% for field in rForm %}
<label for="{{ field.auto_id }}">{{ field.name }}</label>
<input type="text" name="{{ field.html_name }}" id="{{ field.auto_id }}" />
{% endfor %}
Example of fully hands-on approach
<select class="form-select" id="{{ rForm.result.auto_id }}" name="{{ rForm.result.html_name }}" class="form-control">
<option value="POSITIVE">Positive</option>
<option value="NEGATIVE">Negative</option>
</select>
In order to make sure that the inputs are being parsed correctly, add a print statement in your view to print the POST request:
print("Post Request : ", request.POST)
From there you will be able to see if the result field is being picked up correctly or if it's being ignored. Usually when fields get ignored is because they are not named correctly or sometimes it's because they fail validation.
If the rest of the data is saved correctly and just result is being left out then it's almost for sure an issue with the field name because if the form failed validation it would have aborted the entire operation.
P.S. I just noticed that you select input has the class attribute declared twice

Form not submitting CSRF token missing although it is in rendered html

I'm getting a "CSRF token missing" error, when submitting a form in Django.
I'm using django-crispy-forms in combination with htmx.
The form is rendered via render_crispy_form:
def SwapFormView(request, partialform):
ctx = {}
ctx.update(csrf(request))
mitglied = model_to_dict(request.user.mitglied)
if request.method == 'POST':
# do form things
return HttpResponse("""POST""")
else:
form = ChangeDataForm(
initial=mitglied,
partialform=partialform,
)
html = render_crispy_form(form, helper=form.helper, context=ctx)
return HttpResponse(html)
My helper is contructed like this:
def __init__(self, *args, **kwargs):
super(ChangeDataForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'change-form'
self.helper.attrs = {
'hx-post': reverse('back-to-same-url'),
'hx-target': '#change-form',
'hx-swap': 'outerHTML',
}
self.helper.add_input(Submit('submit', 'absenden'))
The thing is, that as soon as I change the helper to a 'normal' POST-Request it works - but I need this to be done via HTMX to only change out the specific parts in the DOM.
Weirdest thing for me is, that the token is rendered into the final html of the form:
<form hx-post="/profil/nameform/" hx-swap="outerHTML" hx-target="#change-form" id="change-form" method="post" enctype="multipart/form-data" class="">
<input type="hidden" name="csrfmiddlewaretoken" value="token-value">
<label for="id_title" class="">Titel</label>
<input type="text" name="title" value="Hau" maxlength="50" class="textinput textInput form-control" id="id_title">
<label for="id_first_name" class=" requiredField">Vorname<span class="asteriskField">*</span> </label>
<input type="text" name="first_name" value="Test" maxlength="50" class="textinput textInput form-control" required="" id="id_first_name">
<label for="id_last_name" class=" requiredField">Nachname<span class="asteriskField">*</span></label>
<input type="text" name="last_name" value="aBVIpzRJoBKP" maxlength="50" class="textinput textInput form-control" required="" id="id_last_name">
<input type="submit" name="submit" value="absenden" class="btn btn-primary" id="submit-id-submit"> </div> </div> </form>
Any help is appreciated...
EDIT:
The problem solved itself after I removed some kwargs I passed from the view to the form. Still don't know, what the real problem was, but I found a workaround.

Why isn't my page redirecting after I submit the form or refresh the page in Django?

I am working on a Django application but I am not getting the desired results. The create_job page is not rendering after I submit the the form or refresh the entire page.
This is the create_job_page view
def create_job_page(request):
current_customer = request.user.customer
if not current_customer.stripe_payment_method_id:
return redirect(reverse('customer:payment_method'))
# return render(request, 'customer/create_job.html')
# Filtering
create_job = Job.objects.filter(customer=current_customer, status=Job.CREATING_STATUS).last()
step1_form = forms.JobCreateStep1Form(instance=create_job)
if request.method == 'POST':
if request.POST.get('step') == '1': #If it's form one
step1_form = forms.JobCreateStep1Form(request.POST, request.FILES)
if step1_form.is_valid():
creating_job = step1_form.save(commit=False)
# Adding current customer to the form
creating_job.customer = current_customer
creating_job.save()
return redirect(reverse('customer:create_job'))
return render(request, 'customer/create_job.html', {
"step1_form": step1_form
})
This is the HTML code
<b>Create a Job</b>
<div class="tab-content" id="pills-Content">
<div class="tab-pane fade" id="pills-info" role="tabpanel" aria-labelledby="pills-info-tab">
<h1>Item Info</h1>
<form method="POST" enctype="multipart/form-data">
<b class="text-secondary">Item Information</b></br>
<div class="card bg-white mt-2 mb-5">
<div class="card-body">
{% csrf_token %}
{% bootstrap_form step1_form %}
</div>
</div>
<input type="hidden" name="step" value="1">
<button class="btn btn-primary" type="submit">Save & Continue</button>
</form>
</div>
In django i never use reverse, i generally just write redirect('app:url_name')

Django Generated Timeslots Selector Required is Always true, How to add value>0

I have a booking system in which i enter dates and timeslots available to book.
the form gets the timeslots from the date and converts it to the user timezone time.
i want the client to select a date and an available timeslot before continuing the form but even with required it doesnt work.
i have a model for timeslots and one for event, date+timeslot
then a form to make client select available date+timeslot, with a html to find timeslot available for each day
html
<option value="">{% if time_slots %}Available Slots{% else %}No Slot Available{% endif %}</option>
{% for time_set in time_slots %}
<option value="{{ time_set.pk }}">{{ time_set.start }} - {{ time_set.end }}</option>
{% endfor %}
models
class TimeSlots(models.Model):
start = models.TimeField(null=True, blank=True)
end = models.TimeField(null=True, blank=True)
class Meta:
ordering = ['start']
def __str__(self):
return '%s - %s' % (self.start.strftime("%I:%M %p"), self.end.strftime("%I:%M %p"))
class Event(models.Model):
event_date = models.DateField()
start = models.ForeignKey(TimeSlots, on_delete=models.CASCADE, verbose_name='Slot Time', null=True)
available = models.BooleanField(default=True)
class Meta:
verbose_name = u'Event'
verbose_name_plural = u'Event'
def __str__(self):
return str(self.event_date)
def get_absolute_url(self):
url = reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[self.pk])
return u'%s' % (url, str(self.start))
form
class PatientForm(forms.ModelForm):
class Meta:
model = Patient
fields = ('patient_name', 'patient_country','phone_number', 'email', 'event_date','start', 'timestamp', 'datestamp')
widgets = {
'event_date': DateInput(),
'patient_country': CountrySelectWidget(),
}
def __init__(self, *args, **kwargs):
super(PatientForm, self).__init__(*args, **kwargs)
self.fields['start'].queryset = TimeSlots.objects.none()
if 'event_date' in self.data:
try:
event_id = self.data.get('event_date')
# event = Event.objects.get(pk=event_id)
self.fields['start'].queryset = TimeSlots.objects.filter(event__event_date=event_id, event__available=True)
except (ValueError, TypeError):
pass # invalid input from the client; ignore and fallback to empty City queryset
elif self.instance.pk:
self.fields['start'].queryset = self.instance.timeslot_set
views
class PatientCreate(CreateView):#was CreateView
form_class = PatientForm
template_name = 'appointment/index.html'
def get_context_data(self, **kwargs): # new
context = super(PatientCreate, self).get_context_data(**kwargs)
context['key'] = settings.STRIPE_PUBLISHABLE_KEY
return context
def load_time_slots(request):
event_date = request.GET.get('event_date')
client_timezone = request.GET.get('timezone')
client_timezone = pytz.timezone(client_timezone)
event_date, original_date = get_original_event_date_by_timezone(client_timezone, event_date)
time_slots = TimeSlots.objects.filter(event__event_date= event_date, event__available=True)
final_time_slots = []
for time_slot in time_slots:
start_time = time_slot.start
original_start_date_time = original_date.replace(hour=start_time.hour, minute=start_time.minute,
second=start_time.second,
tzinfo=original_time_zone)
timezone_start_date_time = original_start_date_time.astimezone(client_timezone)
end_time = time_slot.end
original_end_date_time = original_date.replace(hour=end_time.hour, minute=end_time.minute,
second=end_time.second,
tzinfo=original_time_zone)
timezone_end_date_time = original_end_date_time.astimezone(client_timezone)
final_time_slots.append({'pk': time_slot.pk, 'start': timezone_start_date_time.time,
'end': timezone_end_date_time.time})
return render(request, 'appointment/dropdown_list_options.html', {'time_slots': final_time_slots})
def get_original_event_date_by_timezone(client_timezone, event_date):
client_date = datetime.datetime.strptime(event_date, '%Y-%m-%d')
client_date = client_date.replace(tzinfo=client_timezone)
original_date = client_date.astimezone(original_time_zone)
original_date = original_date.replace(hour=0, minute=0, second=0, microsecond=0)
event_date = original_date.strftime('%Y-%m-%d')
return event_date, original_date
def create_event(request, start_time, day_date):
time_slot = TimeSlots.objects.get(start=start_time)
Event.objects.create(event_date=day_date, start=time_slot)
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
form page html
<div class="container" style="margin-top:50px;margin-bottom:50px;">
<div class="stepwizard col-md-offset-3">
<div class="stepwizard-row setup-panel">
<div class="stepwizard-step">
1
<p>Date & Time</p>
</div>
<div class="stepwizard-step">
2
<p>Information</p>
</div>
<div class="stepwizard-step">
3
<p>Calling Method</p>
</div>
<div class="stepwizard-step">
4
<p>Payment Method</p>
</div>
</div>
</div>
<form role="form" action="{% url 'charge' %}" method="POST" id="patientForm" data-times-url="{% url 'ajax_load_time_slots' %}">
<div class="row setup-content" id="step-1">
<div class="col-xs-6 col-md-offset-3">
<div class="col-md-12">
<h3> Appointments date and time</h3>
<div class="form-group">
<label class="control-label" for="id_event_date">Event Date:</label>
<input class="form-control" type="date" name="event_date" id="id_event_date" required="required" />
</div>
<div class="form-group">
<label class="control-label" for="id_start">{% trans "Time:"%}</label>
<p><select required="required" class="form-control" name="start" style="display:inline;" id="id_start">
<option value="">---------</option></select></p><input type="hidden" name="timezone">
<script>$("#patientForm input[name='timezone']").val(Intl.DateTimeFormat().resolvedOptions().timeZone);</script>
</select></p>
</div>
<button class="btn btn-primary nextBtn btn-lg pull-right" type="button">Next</button>
</div>
</div>
</div>
<div class="row setup-content" id="step-2">
<div class="col-xs-6 col-md-offset-3">
<div class="col-md-12">
<h3> Step 2</h3>
<div class="form-group">
<label for="id_patient_fname" class="control-label">First Name:</label>
<input name="patient_name" id="id_patient_name" required="required" maxlength="100" type="text" class="form-control" placeholder="Enter First Name" />
</div>
<div class="form-group">
<label class="control-label">Last Name:</label>
<input required="required" maxlength="100" type="text" class="form-control" placeholder="Enter Last Name" />
</div>
<div class="form-group">
<label for="id_phone_number" class="control-label">Phone Number:</label>
<input name="phone_number" id="id_phone_number" required="required" maxlength="100" type="text" class="form-control" placeholder="Enter Phone Number" />
</div>
<div class="form-group">
<label for="id_emal" class="control-label">Email:</label>
<input name="email" id="id_email" maxlength="100" type="text" required="required" class="form-control" placeholder="Enter Email" />
</div>
<div class="form-group">
<label class="control-label">City</label>
<textarea required="required" class="form-control" placeholder="Enter your address"></textarea>
</div>
<button class="btn btn-primary nextBtn btn-lg pull-right" type="button">Next</button>
</div>
</div>
</div>
<div class="row setup-content" id="step-3">
<div class="col-xs-6 col-md-offset-3">
<div class="col-md-12">
<div class="form-group">
<label class="control-label">Choose The Way You Want to Receive The Video Call:</label>
<label class="radio-inline"><input class="form-control" type="radio" name="optradio" checked>Skype</label>
<label class="radio-inline"><input class="form-control" type="radio" name="optradio">Whatsapp</label>
</div>
<button class="btn btn-primary nextBtn btn-lg pull-right" type="button">Next</button>
</div>
</div>
</div>
<div class="row setup-content" id="step-4">
<div class="col-xs-6 col-md-offset-3">
<div class="col-md-12">
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button" data-key="pk_test_KPSQTmUOl1DLP2eMc7zlvcnS"
data-description="Buying a 30mn Skype Session" data-amount="3000" data-locale="auto"></script>
</div>
</div>
</div>
</form>
</div>
in the html of the form page, i add required to select but it doesnt work
i need client to select lets say 29/01/2019, then if there is availability choose a timeslote lets say 5.30pm-6.00pm, and then only the next arrow will appear
With CreateView it's a little bit tricky when you want to initialize your ModelForm data. So, instead of doing initialization under your ModelForm, do it under the CreateView class like this example:
Your form:
class PatientForm(forms.ModelForm):
class Meta:
model = Patient
fields = ('patient_name', 'patient_country','phone_number', 'email', 'event_date','start', 'timestamp', 'datestamp')
widgets = {
'event_date': DateInput(),
'patient_country': CountrySelectWidget(),
}
Your view:
class PatientCreate(CreateView):
form_class = PatientForm
template_name = 'appointment/index.html'
initial = {}
def get_initial(self):
base_initial = super().get_initial() # it's a simple dict
# initialize your form's data here
# Your logic ...
return base_initial
# The rest of your logic
...
And, in order to know why you need to do this. CreateView inherits from FormMixin which has initial and get_initial() thus will initialize your form's data instead of doing it under your form.
See this links for more details: CreateView MRO and FormMixin

Receive data from Django front end form

what I am trying to do:
I am trying to get data from Django frontend table, and post it back to frontend.
I am not using modelForm, I thought I don't need save the data to database.
My code:
in views.py:
def panelThree(request):
startDate = ''
endDate = ''
byOtherField = ''
if request.method == 'POST':
form = QueryForm(request.POST)
if form.is_valid():
startDate = request.POST.get('date')
endDate = request.POST.get('date1')
byOtherField = request.POST.get('date2')
else:
form = QueryForm()
# if not byOtherField:
dataTable = Production.objects.order_by('-id')
return render(request, 'frontend/panelThree.html', {
'form': form, 'dataTable': dataTable, 'startDate':startDate
})
frontend_form:
<form method="post">
{% csrf_token %}
<div class="row">
<div class="col-md-8">
<div class="row">
<div class="col-md-4">
<label >
TextField1
</label>
<input class="form-control" id="date" name="date" placeholder="MM/DD/YYYY" type="text"/>
<p> >> Start Date </p>
</div>
<div class="col-md-4">
<label class="control-label " for="date1">
TextField2
</label>
<input class="form-control" id="date1" name="date1" placeholder="MM/DD/YYYY" type="text"/>
<p> >> End Date </p>
</div>
<div class="col-md-4">
<label class="control-label " >
Or
</label>
<label class="control-label " for="date2">
TextField3
</label>
<input class="form-control" id="date2" name="date2" placeholder="MM/DD/YYYY" type="text"/>
<p> >> By Other Field </p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="button-box">
<button class="btn btn-primary " name="submit" type="submit"> Search </button>
</div>
</div>
</div>
</form>
My error:
there is no error, but I just cannot get value from the form post to 'startDate'. It always shows blank value.
I appreciate the help to address this issue, or any ways to work around. Thanks in advance.
2 problems here.
1) The fields in the html are named incorrectly.
<input class="form-control" id="date" name="date" placeholder="MM/DD/YYYY" type="text"/>
The name field here should be set to startDate, and so should the id field.
2) You're grabbing data directly from POST.
After verifying the form is_valid, you should get data from it like this:
startDate = form.cleaned_data['startDate']
By removing the "if form.is_valid()" statement, and changing back to request.POST.get method, it's working
def panelThree(request):
startDate = ''
endDate = ''
byOtherField = ''
if request.method == 'POST':
form = QueryForm(request.POST)
startDate = request.POST.get('startDate')
endDate = request.POST.get('endDate')
byOtherField = request.POST.get('byOtherField')
else:
form = QueryForm()
# if not byOtherField:
dataTable = Production.objects.order_by('-id')
return render(request, 'frontend/panelThree.html', {
'form': form, 'dataTable': dataTable, 'startDate':startDate
})