Dynamic updating & HTMX - django

I have this form setup
This is what I want to happen; when the user selects an "Indoor Manufacturer" it then provides a list of "Indoor Models" that belongs to the "Indoor Manufacturer". The same thing for "Outdoor Manufacturer" & its Corresponding "Outdoor Models"
I can get the above to work fine using HTMX using my code below
However, the next step is to add to the HTMX the "Indoor/ Outdoor Same" checkbox, which will copy the data from "Indoor Manufacturer" to "Outdoor Manufacturer".
I tried implementing the basics of what I know, but it's not quite doing what I want - in that it is copying over the data, but only once on the first load
forms
from django import forms
from.models import Maintenance
from . import models
from units.models import System, Cooler, Make
from installs.models import Install
from bootstrap_datepicker_plus import DatePickerInput, DateTimePickerInput
from dynamic_forms import DynamicField, DynamicFormMixin
class CreateSystem(DynamicFormMixin, forms.ModelForm):
def indoor_choices(form):
owner = form['indoor_manufacturer'].value()
return Cooler.objects.filter(manufacturer=owner, in_out="indoor")
def indoor_initial(form):
owner = form['indoor_manufacturer'].value()
return Cooler.objects.filter(manufacturer=owner, in_out="indoor").first()
def outdoor_choices(form):
owner = form['outdoor_manufacturer'].value()
return Cooler.objects.filter(manufacturer=owner, in_out="outdoor")
def outdoor_initial(form):
owner = form['outdoor_manufacturer'].value()
#print (form['outdoor_manufacturer'].value())
return Cooler.objects.filter(manufacturer=owner, in_out="outdoor").first()
def outdoor_manufacturer_choices(form):
same_mamanufacturer = (form['manufacturer_boolean'].value())
condition = (form['indoor_manufacturer'].value())
if same_mamanufacturer is False:
return Make.objects.all()
else:
return Make.objects.filter(id=condition)
manufacturer_boolean = forms.BooleanField(
initial=True,
widget=forms.CheckboxInput(attrs={
'class':'form-control',
'autocomplete': 'off',
'hx-get': '/companies/indoor_manufacturer/',
'hx-target': '#id_outdoor_manufacturer'}))
indoor_manufacturer = forms.ModelChoiceField(
queryset=Make.objects.all(),
initial=Make.objects.first(),
widget=forms.Select(
attrs={
'class': 'form-control',
'autocomplete': 'off',
'hx-get': '/companies/indoor_coolers/',
'hx-target': '#id_indoor_cooler'}))
outdoor_manufacturer = DynamicField(forms.ModelChoiceField,
queryset=outdoor_manufacturer_choices,
widget=forms.Select(
attrs={
'class': 'form-control',
'autocomplete': 'off',
'hx-get': '/companies/outdoor_coolers/',
'hx-target': '#id_outdoor_cooler'}))
indoor_cooler = DynamicField(forms.ModelChoiceField,
queryset=indoor_choices,
initial=indoor_initial,
widget=forms.Select(
attrs={
'class': 'form-control'}))
outdoor_cooler = DynamicField(forms.ModelChoiceField,
queryset=outdoor_choices,
initial=outdoor_initial,
widget=forms.Select(
attrs={
'class': 'form-control'}))
class Meta:
model = System
fields = ['manufacturer_boolean','outdoor_manufacturer','indoor_manufacturer','indoor_cooler','outdoor_cooler']
urls
from django.urls import path, re_path
from.import views
urlpatterns = [
path('', views.company_list, name='companies'),
path('zones/', views.zone_list, name='zone_list'),
path('zones/<int:id>', views.zone_detail, name='zone_detail'),
path('create/', views.company_create, name="company_create"),
path('<int:id>/', views.company_detail, name="company_detail"),
path('<int:id>/update/', views.company_update, name="company_update"),
path('<int:id>/system_detail/', views.system_detail, name="system_detail"),
path('<int:id>/system_create/', views.system_create, name="system_create"),
path('<int:id>/install_create/', views.install_create, name="install_create"),
path('indoor_coolers/', views.indoor_coolers, name='indoor_coolers'),
path('outdoor_coolers/', views.outdoor_coolers, name='outdoor_coolers'),
path('manufacturer_boolean/', views.manufacturer_boolean, name='manufacturer_boolean'),
path('indoor_manufacturer/', views.indoor_manufacturer, name='indoor_manufacturer'),
]
views
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from.models import Customer, Zone
from units.models import System, Maintenance, Cooler, Make
from units.forms import CreateSystem
from installs.models import Install
from django.http import HttpResponse, JsonResponse
from .import forms
from dateutil.relativedelta import relativedelta
import datetime
def system_create(request, id):
page_live = False
user_active_required = True
if not request.user.userprofile.is_verified and user_active_required:
return redirect('accounts:activate')
elif not request.user.is_staff and not page_live :
return redirect('coming_soon')
else:
owner_id = id
customer = Customer.objects.get(id = id)
if request.method == 'POST':
form = CreateSystem(request.POST)
if form.is_valid():
#add this data later
else:
form = CreateSystem()
return render(request, 'systems/system_create.html',{'form':form, 'owner_id':owner_id,
'customer':customer,})
def indoor_coolers(request):
form = CreateSystem(request.GET)
return HttpResponse(form['indoor_cooler'])
def outdoor_coolers(request):
form = CreateSystem(request.GET)
return HttpResponse(form['outdoor_cooler'])
def manufacturer_boolean(request):
form = CreateSystem(request.GET)
return HttpResponse(form['manufacturer_boolean'])
def indoor_manufacturer(request):
form = CreateSystem(request.GET)
return HttpResponse(form['indoor_manufacturer'])

I added a "change" trigger for both #id_outdoor_manufacturer & #id_manufacturer_boolean, so it listens to any changes then runs the outdoor_manufacturer_choices if/ else conditions

Related

How to remove an already selected option from options list to avoid double bookings

I've been scratching my head with this for 2 days hoping for a sudden brainwave and getting nowhere. I've completely drawn a blank with my logic and all attempts have resulted in me breaking my code. I'm trying to get it so that on a specific date, if a user has already selected a time slot with a selected barber, that that time slot will be removed from the list of time slots, so it cannot be selected again by another user.
From models.py
from django.db import models
import datetime
from django.core.validators import MinValueValidator
from django.contrib.auth.models import User
SERVICES = (
('Gents Cut', 'Gents Cut'),
('Kids Cut', 'Kids Cut'),
('Cut and Shave', 'Cut and Shave'),
('Shave Only', 'Shave Only'),
)
TIME_SLOTS = (
('9.00 - 10.00', '9.00 - 10.00'),
('10.00 - 11.00', '10.00 - 11.00'),
('11.00 - 12.00', '11.00 - 12.00'),
('12.00 - 13.00', '12.00 - 13.00'),
('13.00 - 14.00', '13.00 - 14.00'),
('14.00 - 15.00', '14.00 - 15.00'),
('15.00 - 16.00', '15.00 - 16.00'),
('16.00 - 17.00', '16.00 - 17.00'),
('17.00 - 18.00', '17.00 - 18.00'),
)
BARBER_NAME = (
('Nathan', 'Nathan'),
('Chris', 'Chris'),
('Ben', 'Ben'),
('Dan', 'Dan'),
)
class Booking(models.Model):
date = models.DateField(validators=[MinValueValidator(datetime.date.today)])
time = models.CharField(max_length=50, null=True, choices=TIME_SLOTS)
barber = models.CharField(max_length=50, null=True, choices=BARBER_NAME)
service = models.CharField(max_length=50, null=True, choices=SERVICES)
customer = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return f"{self.customer} has booked {self.service} on {self.date} at {self.time} with {self.barber}"
from views.py
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.views import generic, View
from django.contrib import messages
from django.http import HttpResponseRedirect
from .models import Booking
from .forms import BookingForm
#login_required()
def make_booking(request):
if request.method == "POST":
form = BookingForm(request.POST)
if form.is_valid():
booking_form = form.save(commit=False)
booking_form.customer = request.user
booking_form.save()
messages.success(request, ('Your booking is awaiting confirmation'))
return HttpResponseRedirect('/bookings')
else:
form = BookingForm()
return render(request, 'bookings.html', {'form': form})
from forms.py
from .models import Booking
import datetime
from django import forms
class DatePicker(forms.DateInput):
input_type = 'date'
class BookingForm(forms.ModelForm):
class Meta:
model = Booking
fields = ('date', 'time', 'barber', 'service',)
widgets = {
'date': DatePicker(),
}
One way or another you will need another view to accept the selected date and return the available time slots. If you were to do this via AJAX then your view might look something like this:
import json
from datetime import datetime
def get_time_slots(request):
# get the date in python format, sent via ajax request
date = datetime.strptime(request.GET.get('date'), '%Y-%m-%d')
# get all times from bookings on the provided date
booked_slots = Booking.objects.filter(date=date).values_list('time', flat=True)
available_slots = []
for slots in TIME_SLOTS:
if slots[0] not in booked_slots:
available_slots.append(slots)
times_as_json = json.dumps(available_slots)
return JsonResponse(times_as_json)
A better way, imo, would be to create new models for time slots, services, and barbers. Firstly, you are not hardcoding information and can dynamically change things in the DB. Secondly, you can generate a very slick query to get the available time slots for a day, barber, service, etc. You can also set general availability by linking certain barbers to certain time slots, depending on the hours they work.

Forgot to delete model objects before altering model fields in models.py

I am getting this error continuously when I try to open this database through django admin
Exception Type: OperationalError at /admin/tasks/task/
Exception Value: no such column: tasks_task.task_name_id
Initially I changed a lot a fields in models which already had some objects stored. Now I couldnt have gone back so i started a new app and copied all the files from old app as they were in the same project. deleted the old app and renamed the new app by the name of old one so that in the project wherever refrenced there wont be any problem.
but now whenever i try to migrate i get this error:
File "C:\Users\sarda\anaconda3\envs\myDjangoEnv\lib\site-packages\django\db\backends\sqlite3\base.py", line 326, in check_constraints
raise utils.IntegrityError(
django.db.utils.IntegrityError: The row in table 'tasks_task' with primary key '1' has an invalid foreign key: tasks_task.task_name_id cont
ains a value 'task_name_id' that does not have a corresponding value in tasks_task_list.id.
models.py:
from django.db import models
import datetime
from django.utils import timezone
class Task_List(models.Model):
task_name=models.CharField(max_length=100)
c1=models.CharField(max_length=30, default="OTHER")
c2=models.CharField(max_length=30, default="OTHER")
c3=models.CharField(max_length=30, default="OTHER")
time_esc=models.IntegerField(default=1)
def __str__(self):
return self.task_name
class Task_manager(models.Manager):
def create_Task(self, title,deadline):
Task1 = self.create(title=title,deadline=deadline)
# do something with the book
return Task1
class Task(models.Model):
STATUS = (
('ONGOING', 'ONGOING'),
('COMPLETED','COMPLETED'),
('PENDING','PENDING' ),
('FINISHED','FINISHED')
)
task_name=models.ForeignKey(Task_List, on_delete=models.SET_NULL, null=True)
title=models.CharField(max_length=30,default="Other", blank=True)
created=models.DateTimeField(auto_now_add=True)
deadline_fixed=models.DateTimeField(default=timezone.now())
deadline=models.DateTimeField(default=timezone.now())
score=models.IntegerField(default=0)
score_fixed=models.IntegerField(default=0, editable=False)
status=models.CharField(max_length=15,default="ONGOING",choices=STATUS)
stage=models.CharField(max_length=15, default='NORMAL')
objects=Task_manager()
class Meta:
ordering = [ '-created']
def __str__(self):
return self.title
admin.py:
from django.contrib import admin
from django.utils.translation import ugettext_lazy
# Register your models here.
from .models import *
from django.shortcuts import render, redirect
from .forms import *
from searchableselect.widgets import SearchableSelect
def mark_completed(modeladmin, request, queryset):
refresh(modeladmin,request,queryset)
tasks=Task.objects.all()
form=TaskForm()
for task in tasks:
if task.status=='ONGOING' and timezone.now()<task.deadline:
queryset.update(status='COMPLETED')
elif task.status=="PENDING":
queryset.update(status='FINISHED')
#row_completed=queryset.update(status='FINISHED')
'''message_bit = "%s stories were" % row_completed
self.message_user(request, "%s successfully marked as published." % message_bit)'''
mark_completed.short_description = "Mark COMPLETED"
def refresh(modeladmin, request, queryset):
tasks=Task.objects.all()
form=TaskForm()
for task in tasks:
if task.status=="ONGOING" and task.deadline<timezone.now():
queryset.update(status='PENDING')
refresh.short_description = "Refresh"
class TaskAdmin(admin.ModelAdmin):
'''def film_status(self, obj):
if obj.status == 'FINISHED':
return '<div style="width:100%%; height:100%%; background-color:Green;">%s</div>' % obj.status()
return obj.status()
film_status.allow_tags = True'''
form=TaskForm
list_display=('task_name','title','status','deadline')
list_filter=('status',)
search_fields=('title',)
date_hierarchy='created'
actions=[refresh,mark_completed]
radio_fields={'status': admin.HORIZONTAL}
list_editable=['status',]
#raw_id_fields=('c2',)
#auto_complete=['title']
'''fieldsets=[
['Basic', {
'fields': ['comment','status']}],
['Advanced', {
'classes': ['collapse'],
'fields': [('created','deadline_fixed'),('c1', 'c2','c3'),('score_fixed','score')],
}],]'''
readonly_fields = ['score','score_fixed','deadline_fixed','created']
def change_view(self,request,object_id,extra_content=None):
return super(TaskAdmin,self).change_view(request,object_id)
admin.site.register(Task,TaskAdmin)
If you are using sqlite and don't want to do everything with command line, you can access it with DB Browser.

Django-CMS 3.0.3 Publishing a page duplicates data from plugin django-cms-saq

Django-cms-saq is tested for 2.4.x. I'm trying to update the program to work with 3.0.X.
So far, I've updated all the imports but am coming across an unusual error. When I add a question (a plugin) to a page and hit publish, it creates two copies of the question in the database (viewable through the admin site). Deleting either copy removes both from the published page but leaves the question in edit mode.
How would I go about trouble shooting this?
I will include some of the files here. Please let me know if you need any other files.
Note that I am trying to add a multiple-choice question.
From models.py:
from django.db import models
from django.db.models import Max, Sum
from cms.models import CMSPlugin, Page, Placeholder
from cms.models.fields import PageField
from taggit.managers import TaggableManager
from djangocms_text_ckeditor.models import AbstractText
...
class Question(CMSPlugin):
QUESTION_TYPES = [
('S', 'Single-choice question'),
('M', 'Multi-choice question'),
('F', 'Free-text question'),
]
slug = models.SlugField(
help_text="A slug for identifying answers to this specific question "
"(allows multiple only for multiple languages)")
tags = TaggableManager(blank=True)
label = models.CharField(max_length=512, blank=True)
help_text = models.CharField(max_length=512, blank=True)
question_type = models.CharField(max_length=1, choices=QUESTION_TYPES)
optional = models.BooleanField(
default=False,
help_text="Only applies to free text questions",
)
depends_on_answer = models.ForeignKey(
Answer, null=True, blank=True, related_name='trigger_questions')
def copy_relations(self, oldinstance):
for answer in oldinstance.answers.all():
answer.pk = None
answer.question = self
answer.save()
self.depends_on_answer = oldinstance.depends_on_answer
#staticmethod
def all_in_tree(page):
root = page.get_root()
# Remember that there might be questions on the root page as well!
tree = root.get_descendants() | Page.objects.filter(id=root.id)
placeholders = Placeholder.objects.filter(page__in=tree)
return Question.objects.filter(placeholder__in=placeholders)
#staticmethod
def all_in_page(page):
placeholders = Placeholder.objects.filter(page=page)
return Question.objects.filter(placeholder__in=placeholders)
def score(self, answers):
if self.question_type == 'F':
return 0
elif self.question_type == 'S':
return self.answers.get(slug=answers).score
elif self.question_type == 'M':
answers_list = answers.split(',')
return sum([self.answers.get(slug=a).score for a in answers_list])
#property
def max_score(self):
if not hasattr(self, '_max_score'):
if self.question_type == "S":
self._max_score = self.answers.aggregate(
Max('score'))['score__max']
elif self.question_type == "M":
self._max_score = self.answers.aggregate(
Sum('score'))['score__sum']
else:
self._max_score = None # don't score free-text answers
return self._max_score
def percent_score_for_user(self, user):
if self.max_score:
try:
score = Submission.objects.get(
question=self.slug,
user=user,
).score
except Submission.DoesNotExist:
return 0
return 100.0 * score / self.max_score
else:
return None
def __unicode__(self):
return self.slug
...
From cms_plugins.py
import itertools
import operator
from django.contrib import admin
from django.utils.translation import ugettext as _
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from cms_saq.models import Question, Answer, GroupedAnswer, Submission, \
FormNav, ProgressBar, SectionedScoring, ScoreSection, BulkAnswer, \
QuestionnaireText, SubmissionSetReview
from djangocms_text_ckeditor.cms_plugins import TextPlugin
from djangocms_text_ckeditor.models import Text
from bs4 import BeautifulSoup
...
class QuestionPlugin(CMSPluginBase):
model = Question
module = "SAQ"
inlines = [AnswerAdmin]
exclude = ('question_type',)
def render(self, context, instance, placeholder):
user = context['request'].user
submission_set = None
triggered = True
depends_on = None
if instance.depends_on_answer:
depends_on = instance.depends_on_answer.pk
try:
Submission.objects.get(
user=user,
question=instance.depends_on_answer.question.slug,
answer=instance.depends_on_answer.slug,
submission_set=submission_set,
)
triggered = True
except:
triggered = False
extra = {
'question': instance,
'answers': instance.answers.all(),
'triggered': triggered,
'depends_on': depends_on,
}
if user.is_authenticated():
try:
extra['submission'] = Submission.objects.get(
user=user,
question=instance.slug,
submission_set=submission_set,
)
except Submission.DoesNotExist:
pass
context.update(extra)
return context
def save_model(self, request, obj, form, change):
obj.question_type = self.question_type
super(QuestionPlugin, self).save_model(request, obj, form, change)
...
class MultiChoiceQuestionPlugin(QuestionPlugin):
name = "Multi Choice Question"
render_template = "cms_saq/multi_choice_question.html"
question_type = "M"
exclude = ('question_type', 'help_text')
...
plugin_pool.register_plugin(SingleChoiceQuestionPlugin)
plugin_pool.register_plugin(MultiChoiceQuestionPlugin)
plugin_pool.register_plugin(DropDownQuestionPlugin)
plugin_pool.register_plugin(GroupedDropDownQuestionPlugin)
plugin_pool.register_plugin(FreeTextQuestionPlugin)
plugin_pool.register_plugin(FreeNumberQuestionPlugin)
plugin_pool.register_plugin(FormNavPlugin)
plugin_pool.register_plugin(SubmissionSetReviewPlugin)
plugin_pool.register_plugin(SectionedScoringPlugin)
plugin_pool.register_plugin(ProgressBarPlugin)
plugin_pool.register_plugin(BulkAnswerPlugin)
plugin_pool.register_plugin(SessionDefinition)
plugin_pool.register_plugin(QuestionnaireTextPlugin)
plugin_pool.register_plugin(TranslatedTextPlugin)
From cms_app.py:
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
from django.utils.translation import ugettext_lazy as _
class CMSSaq(CMSApp):
name = _("Self Assessment")
urls = ["cms_saq.urls"]
apphook_pool.register(CMSSaq)
Additional information:
This duplicative observation creates an issue when trying get the question object via its slug Question.objects.get(slug=question_slug). Such a query should only return one question. What we get here is two questions returned.
import re
from django.http import HttpResponse, HttpResponseBadRequest
from django.contrib.auth.decorators import login_required
from django.views.decorators.http import require_POST, require_GET
from django.views.decorators.cache import never_cache
from django.utils import simplejson, datastructures
from django.conf import settings
from cms_saq.models import Question, Answer, Submission, SubmissionSet
ANSWER_RE = re.compile(r'^[\w-]+(,[\w-]+)*$')
#require_POST
def _submit(request):
post_data = datastructures.MultiValueDict(request.POST)
submission_set_tag = post_data.pop('submission_set_tag', '')
for question_slug, answers in post_data.iteritems():
# validate the question
try:
question = Question.objects.get(
slug=question_slug,
#placeholder__page__publisher_is_draft=False,
)
except Question.DoesNotExist:
return HttpResponseBadRequest(
"Invalid question '%s'" % question_slug,
)
# check answers is a list of slugs
if question.question_type != 'F' and not ANSWER_RE.match(answers):
return HttpResponseBadRequest("Invalid answers: %s" % answers)
# validate and score the answer
try:
score = question.score(answers)
except Answer.DoesNotExist:
return HttpResponseBadRequest(
"Invalid answer '%s:%s'" % (question_slug, answers)
)
# save, but don't update submissions belonging to an existing set
filter_attrs = {
'user': request.user.id,
'question': question_slug,
'submission_set': None,
}
attrs = {'answer': answers, 'score': score}
rows = Submission.objects.filter(**filter_attrs).update(**attrs)
if not rows:
attrs.update(filter_attrs)
Submission.objects.create(**attrs)
# Create submission set if requested
if submission_set_tag:
submission_set_tag = submission_set_tag[0]
if submission_set_tag:
_create_submission_set(
request, submission_set_tag
)
return HttpResponse("OK")
If you create a page in CMS, and add plugins to it, once you hit publish the CMS creates a copy of each of those plugins as they are at that point in time. This gives the page a live version, and enables you to then make changes to it which are held in draft mode, until you hit publish again.
This allows you to have a draft version of the site where edits/changes can be made and finalised before making them public.
Seeing two copies for each plugin therefore, isn't a problem.
As a side note, I'd strongly recommend you join the CMS user group on Google Plus if you're developing CMS sites; https://plus.google.com/communities/107689498573071376044
update
Ok, so a plugin in CMS is attached to a placeholder which is on a page, and it's the page that has two version of itself. Because each plugin attaches to a page, you can use that relationship to filter your plugins.
If you register your plugin with admin, or to object calls on it you're just looking at what is stored in your table, which as I say, includes the draft and live version of plugins.
So when you're querying for your plugin do this;
questions = Question.objects.filter(placeholder__page__publisher_is_draft=True)
This will get you all the questions which are attached to draft pages.
The only drawback to this is that plugins are guaranteed to be attached to a Page() object unless the plugin is set to page_only = True but I've only ever been interested in plugins attached to pages so it works for me. See the docs for more info on this.
Furthermore, if you're ever adding CMS page links to any of your plugins, please don't forget to add a similar argument to your model field to ensure that you limit the page objects to draft only;
page_link = models.ForeignKey(
Page,
limit_choices_to={'publisher_is_draft': True},
help_text=_("Link to another page on the site."),
on_delete=models.SET_NULL,
related_name='myapp_page_link'
)

Get_FOO_display method for form data passed to send_mail

I have a basic form that captures user-submitted data, and sends it in an email via Django's mail module.
But I'd like for the email's subject and message to contain the human-readable version of the choice for order_type.
How do I modify my view to show this?
Here's my form:
from django import forms
from django.contrib.localflavor.us.forms import USPhoneNumberField
import datetime
class OrderForm(forms.Form):
CAKE = 'CA'
CHEESECAKE = 'CH'
COFFEECATERING = 'CO'
BREAKFASTPLATTER = 'BR'
COOKIEPLATTER = 'CP'
GOURMETDESSERTSPLATTER = 'GD'
ORDER_TYPE_CHOICES = (
(CAKE, 'Cake'),
(CHEESECAKE, 'Cheesecake'),
(COFFEECATERING, 'Coffee catering'),
(BREAKFASTPLATTER, 'Breakfast platter'),
(COOKIEPLATTER, 'Cookie platter'),
(GOURMETDESSERTSPLATTER, 'Gourmet desserts platter'),
)
name = forms.CharField(max_length=100, required=True, help_text='100 characters max.')
phone_number = USPhoneNumberField(required=True, help_text='This will allow us to contact you in case of questions regarding your order. Please include your area code.')
email_address = forms.EmailField(required=True, help_text='We will e-mail you to confirm we have received your order, and submitted it to our bakers and baristas.')
date_needed = forms.SplitDateTimeField(required=True, help_text='We can only accept online orders that are placed three days in advance.')
order_description = forms.CharField(required=True, help_text='Please describe your order.')
order_type = forms.ChoiceField(required=True, choices=ORDER_TYPE_CHOICES)
And here's my view:
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from order_form.forms import OrderForm
def order(request):
if request.method == 'POST':
form = OrderForm(request.POST)
if form.is_valid():
name = form.cleaned_data['name']
order_description = form.cleaned_data['order_description']
phone_number = form.cleaned_data['phone_number']
email_address = form.cleaned_data['email_address']
date_needed = form.cleaned_data['date_needed']
order_type = form.cleaned_data['order_type']
subject = "Order placed for %s by %s" % (order_type, name)
message = "Name: %s\nPhone number: %s\nEmail address: %s\nOrder description: %s\nDate needed: %s" % (name, phone_number, email_address, order_description, date_needed)
from django.core.mail import send_mail
send_mail(subject, message, 'orders#sitename.com', ['anotherperson#sitename.com', email_address])
return HttpResponseRedirect('thanks/')
else:
form = OrderForm()
return render(request, 'order_form/order.html', {
'form': form,
})
I usually just write a simple method for this:
def human_order_type(order_id):
for a_tuple in OrderForm.ORDER_TYPE_CHOICES:
if order_id in a_tuple:
return a_tuple[1]
return None

django dynamic form generation

say I have a model such:
class ComponentLength(models.Model):
component_name = models.CharField(max_length=155)
length1 = models.IntegerField()
length2 = models.IntegerField()
length3 = models.IntegerField()
length4 = models.IntegerField()
Now I have a form for the user to select a component, and on the next page I want to display 4 checkboxes for the various length options, which are different for different components.
What is the best way in Django to generate the form with these checkboxes based on the component name (accessible in session data) already selected by the user.
Any help much appreciated.
You could use a normal django form and change its fields upon instantiation, in the init method. Something like this:
class SecondForm(forms.Form):
def __init__(self, *args, **kwargs):
super(SecondForm, self).__init__(*args, **kwargs)
try:
#get the object, something like this:
obj_ = ComponentLength.objects.get(component_name = session.get('component_name_or_whatever_you_stored'))
except:
#handle the error case, e.g:
return
self.fields['length1'] = forms.CheckboxInput(attrs={'value' : obj_.length1 })
self.fields['length2'] = forms.CheckboxInput(attrs={'value' : obj_.length2 })
self.fields['length3'] = forms.CheckboxInput(attrs={'value' : obj_.length3 })
self.fields['length4'] = forms.CheckboxInput(attrs={'value' : obj_.length4 })
#Consider using a hidden input instead of polluting the session variables
#with form data
self.fields['component_length'] = forms.HiddenInput(attrs={'value' : obj_.pk})
The above code is not tested, but I expect it should work. Please let me know how it goes.
Form wizards is exactly what you need.
https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/
See the example shown here https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/#usage-of-namedurlwizardview
And how forms are defined here https://docs.djangoproject.com/en/1.7/ref/contrib/formtools/form-wizard/#conditionally-view-skip-specific-steps
I haven't tested the code below, but it should be something similar to the following:
myapp/forms.py
from django.forms import ModelForm
from myapp.models import ComponentLength
class ComponentLengthNameForm(ModelForm):
class Meta:
model = ComponentLength
fields = ['component_name',]
class ComponentLengthChoicesForm(ModelForm):
class Meta:
model = ComponentLength
fields = ['length1', 'length2', 'length3', 'length4',]
myapp/views.py
from django.contrib.formtools.wizard.views import SessionWizardView
from django.shortcuts import render_to_response
class ComponentWizard(SessionWizardView):
def done(self, form_list, **kwargs):
return render_to_response('done.html', {
'form_data': [form.cleaned_data for form in form_list],
})
myapp/urls.py
from django.conf.urls import url, patterns
from myapp.forms import ComponentLengthNameForm, ComponentLengthChoicesForm
from myapp.views import ContactWizard
named_contact_forms = (
('name', ComponentLengthNameForm),
('length-choices', ComponentLengthChoicesForm),
)
component_wizard = ComponentWizard.as_view(named_contact_forms,
url_name='component-wizard-form-step', done_step_name='finished')
urlpatterns = patterns('',
url(r'^my-form/(?P<step>.+)/$', component_wizard, name='component-wizard-form-step'),
url(r'^my-form/$', component_wizard, name='component-wizard-form'),
)