I am attempting to create a multi-step ModelForm using a library called FormWizard. However, I am having difficulty getting the results I want.
Work Flow
User clicks on create patient link
User opens patient form
User inserts patient data
User submits form
if form is invalid:
redisplay form with error
if form is valid:
move to next form
enter symptom data
submit form
if form is invalid:
redisplay form with error
if form is valid:
move to next form
enter disease data
submit form
if form is invalid:
redisplay form with error
if form is valid:
User is redirected to general form that has patient + symptom + disease information.
View that will create the multi-step ModelForm
class ConsultationCreate(SessionWizardView):
instance = None
def get_form_instance(self, step):
if self.instance is None:
self.instance = Patient
return self.instance
template_ = 'data/multi_form.html'
def done(self, form_list, **kwargs):
self.instance.save()
def process_form_data(form_list):
form_data = [form.cleaned_data for form in form_list]
logr.debug(form_data[0]['NIS'])
logr.debug(form_data[1]['identity'])
logr.debug(form_data[2]['identity'])
return form_data
This is the error it produces:
ValidationError at /patient/create
['ManagementForm data is missing or has been tampered.']
The code
urls.py
from django.conf.urls import url
from data.views import IdentityList, IdentityCreate, IdentitySpecific, ConsultationCreate, MultiStep
from data.forms import SymptomForm, DiseaseForm, IdentityForm
urlpatterns = [
url(r'^patient/$', IdentityList.as_view(), name = 'data_patient_list'),
url(r'^patient/create$', ConsultationCreate.as_view([IdentityForm, SymptomForm, DiseaseForm]), name = 'data_patient_create'),
]
models.py
from django.db import models
from django.utils import timezone
from django.shortcuts import reverse
import datetime
Creates Patient database
class Patient(models.Model):
NIS =models.CharField(max_length = 200, primary_key = True)
timestamp = models.DateTimeField(auto_now = True)
first_name = models.CharField(max_length = 80, null = True)
last_name = models.CharField(max_length = 80, null = True )
contact = models.CharField(max_length = 15, null = True)
location = models.CharField(max_length = 100, blank = True)
email_address = models.EmailField()
CHOOSE = 'The patient is'
MALE = 'M'
FEMALE = 'F'
personGender = ((CHOOSE,'The patient is'),(MALE, 'Male'),(FEMALE,'Female'),)
gender = models.CharField(max_length=2,choices=personGender,default=CHOOSE)
born = models.DateField(auto_now = False, auto_now_add = False, blank = True, null = True)
Creates Symptom database
class Symptom(models.Model):
identity = models.CharField('Name of symptom', max_length = 80, default = '')
description = models.TextField(max_length = 1000, default = '')
patient = models.ManyToManyField(Patient, through = 'Consultation')
def __str__(self):
return '%s' % ( self.identity )
Creates Disease database
class Disease(models.Model):
identity = models.CharField('Name of disease', max_length = 80, default = '')
description = models.TextField(max_length = 5000, default = '')
symptom = models.ManyToManyField(Symptom)
def __str__(self):
return '%s' % ( self.identity )
Consultation model is the intermediate model
class Consultation(models.Model):
patient_identity = models.ForeignKey(Patient)
patient_condition = models.ForeignKey(Symptom)
patient_disease = models.ForeignKey(Disease)
patient_treatment = models.ForeignKey(Treatment)
consultation_date_and_time = models.DateTimeField('visitation date and time', auto_now = False, auto_now_add = False, blank = True, null = True)
# Use get_absolute_url to access single page model objects that possess a Canonical Uniform resource locator
def get_absolute_url(self):
return reverse('data_patient_detail', kwargs={'pk':self.pk})
views.py
from data.models import Treatment, Symptom, Disease, Consultation, Patient
from django.shortcuts import get_object_or_404, render, redirect, render_to_response
from django.views.generic import View
from data.forms import IdentityForm
from formtools.wizard.views import SessionWizardView
Class based view for patient list
class IdentityList(View):
template_ = 'data/patient_list.html'
def get(self, request):
patient_list = Consultation.objects.all()
return render(request, self.template_, {'patient_list':patient_list})
Class based view for patient detail
class IdentitySpecific(View):
model = Consultation
template_ = 'data/patient_detail.html'
def get(self, request, pk):
patient_consult = Consultation.objects.filter(pk=pk)
return render(request, self.template_, {'patient_consult':patient_consult})
Class based view for multi-step modelform
class ConsultationCreate(SessionWizardView):
instance = None
def get_form_instance(self, step):
if self.instance is None:
self.instance = Patient
return self.instance
template_ = 'data/multi_form.html'
def done(self, form_list, **kwargs):
self.instance.save()
Function process_form_data using to process form data in
ConsultationCreate view
def process_form_data(form_list):
form_data = [form.cleaned_data for form in form_list]
logr.debug(form_data[0]['NIS'])
logr.debug(form_data[1]['identity'])
logr.debug(form_data[2]['identity'])
return form_data
Related
I have a model form that have multiple choice fields. using AJAX to update form choic fields upon changed field.
Model:
class Student(models.Model):
CLASSROOM = 0
GROUPROOM = 1
HOMEROOM = 3
STUDENT_RECORD_TYPES = [
(CLASSROOM,_("Classroom")),
(GROUPROOM,_("Group")),
(HOMEROOM,_("Home Edu")),
]
school = models.ForeignKey(School,on_delete=models.CASCADE,blank=False,related_name='student_records')
grade = models.ForeignKey(Grade,on_delete=models.CASCADE,blank=False,related_name="student_records")
record_type = models.PositiveSmallIntegerField(_("Record Type"),choices=STUDENT_RECORD_TYPES,default=0)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['school','grade', 'record_type'],
name='unique_school_grade_record'
),
]
def __str__(self):
return "Record ID: {}".format(self.pk)
Views.py:
def update_students(request,pk):
updated_table=None
student_record = get_object_or_404(Student,pk=pk)
if request.POST:
form = StudentForm(request.POST or None,instance=student_record)
if form.is_valid():
form.save()
messages.success(request,_("Student record Updated Successfully!"))
#Getting data for view
updated_table = update_students_table(request)
else:
messages.error(request,_("Invalid Input, Please check!"))
else:
form = StudentForm(request.GET or None,instance=student_record)
context = {}
# load form template
context['form'] = form
form_template_path = "components/forms/student_update.html"
html_form = loader.render_to_string(form_template_path, context, request)
context['form'] = html_form
return JsonResponse(context)
Forms.py:
class StudentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StudentForm, self).__init__(*args, **kwargs)
for visible in self.visible_fields():
visible.field.widget.attrs['class'] = 'form-control'
self.fields['school'].widget.attrs['class'] += ' select2'
#the issue stands here
#self.data.get('school') returns none even if its exist in form data
if self.data.get('school'):
self.fields['grade'].queryset = Grade.objects.filter(school=self.data.get('school'))
else:
self.fields['grade'].queryset = Grade.objects.none()
class Meta:
model = Student
fields = '__all__'
the strange behavior drives me crazy because when I reselect the school it updates the grade choices normally (with no option selected!), but when I open edit instance form the data is there but grade field have no options in it!
I'm currently working on a Django app that will parse the contents of an uploaded log file to the associated database in my Django project. I've managed to get it all running as expected except it won't associate my uploaded data with the model's ForeignKey. I can assign null=True which resolves the integrity error but then of course, it doesn't assign any of the uploaded data to that ForeignKey. Here's the code:
models.py
class Case(models.Model):
case_ref = models.CharField(max_length=8)
oic = models.CharField(max_length=50)
subject = models.CharField(max_length=100)
submitted_date = models.DateTimeField(default=datetime.now, blank=True)
def get_absolute_url(self):
return reverse('case_list', kwargs={'pk': self.pk})
def __str__(self):
return self.case_ref + " " + self.subject
class TeamviewerLogs(models.Model):
case = models.ForeignKey(Case, on_delete=models.DO_NOTHING)
teamviewer_id = models.IntegerField()
teamviewer_name = models.TextField()
connection_start = models.TextField()
connection_end = models.TextField()
local_user = models.TextField()
connection_type = models.TextField()
unique_id = models.TextField()
def get_absolute_url(self):
return reverse('case_list', kwargs={'pk': self.pk})
def __str__(self):
return str(self.teamviewer_id) + " - " + str(self.teamviewer_id)
forms.py
class UploadLog(forms.ModelForm):
file = forms.FileField()
class Meta:
model = TeamviewerLogs
fields = [
'file'
]
views.py
def add_logs(request, pk):
case = get_object_or_404(Case, pk=pk)
if request.method == 'POST':
form = UploadLog(request.POST, request.FILES)
if form.is_valid():
teamviewer = form.save(commit=False)
teamviewer.case = case
log_file = request.FILES['file']
log_file = filter(None, (line.rstrip() for line in log_file))
for lines in log_file:
split = lines.decode('utf-8').split('\t')
teamviewer_id = split[0]
teamviewer_name = split[1]
connection_start = split[2]
connection_end = split[3]
local_user = split[4]
connection_type = split[5]
unique_id = split[6]
teamviewer = TeamviewerLogs(teamviewer_id=teamviewer_id, teamviewer_name=teamviewer_name,
connection_start=connection_start, connection_end=connection_end,
local_user=local_user, connection_type=connection_type, unique_id=unique_id)
teamviewer.save()
return redirect('tv_log_details', pk=case.pk)
form.save()
else:
form = UploadLog()
return render(request, 'teamviewer/add_logs.html', {'form': form})
But when I click to upload the file I'm hit with:
When it tries to execute teamviewer.save().
I've been trying to resolve this issue for hours and have tried so many different variations of answers from Stackoverflow or previous code I've used that has worked for different models but I've hit a brick wall...hard!
Any help anyone can offer would be greatly appreciated.
Ok, so here's an example of the concept I've suggested in the comments.
I've got a view which passes some data to the a form;
class ListingDetailView(DetailView):
""" Listing detail page """
model = Listing
template_name = 'listing.html'
def get_form_kwargs(self):
"""Return the kwargs for the form"""
kwargs = {}
initial = {
'listing': self.object,
}
kwargs['initial'] = initial
return kwargs
def get_form(self):
form = ApplicationSignupForm(
**self.get_form_kwargs()
)
return form
def get_context_data(self, **kwargs):
""" Add our form to the context """
context = super().get_context_data(**kwargs)
context['form'] = self.get_form()
return context
The form then makes use of that initial data and sets the field it relates to as hidden. I don't validate this data, but I'll try to show how you might do that;
class ApplicationSignupForm(forms.ModelForm):
class Meta:
""" Setup the form """
fields = (
'listing',
...
)
model = Application
widgets = {
'listing': forms.HiddenInput()
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
initial_data = kwargs['initial']
self.listing = initial_data.get('listing')
def clean(self):
"""
Custom form cleaning
"""
cleaned_data = super().clean()
listing = cleaned_data.get('listing')
if listing != self.listing:
self.add_error('listing', "You can't modify this value")
return cleaned_data
models.py
from django.db import models
from django.contrib.auth.models import User
CHOICES = (('1','Earned Leave'),('2','Casual Leave'),('3','Sick Leave'),('4','Paid Leave'))
class Leave(models.Model):
name = models.CharField(max_length = 50)
user = models.ForeignKey(User, on_delete = models.CASCADE, null =True)
employee_ID = models.CharField(max_length = 20)
department = models.CharField(max_length = 50)
designation = models.CharField(max_length = 50)
type_of_leave = models.CharField(max_length = 15, choices = CHOICES, default = None)
from_date = models.DateField(help_text = 'mm/dd/yy')
to_date = models.DateField(help_text = 'mm/dd/yy')
reporting_manager = models.CharField(max_length = 50, default = None, help_text = '0001_manager, 0002_manager')
reason = models.CharField(max_length= 180)
accepted = models.BooleanField(('approval status'), default= False)
def __str__(self):
return self.name
I want the user to only access the 'type_leave_field' after submitting the form, when he is given 'is staff' status.
forms.py
from django import forms
from lrequests import models
class LeaveRequestForm(forms.ModelForm):
class Meta:
fields = ("name", "employee_ID", "department", "designation", "type_of_leave", "from_date", "to_date", "reporting_manager", "reason")
model = models.Leave
views.py
from django.shortcuts import render
from django.http import HttpResponse
from .forms import LeaveRequestForm
from .models import Leave
def leaveRequest(request):
form_class = LeaveRequestForm
if request.method == "POST":
form = LeaveRequestForm(request.POST)
if form.is_valid():
leave = form.save(commit = False)
leave.user = request.user
form.save()
return HttpResponse("Sucessfully submitted")
else:
form = LeaveRequestForm()
return render(request, "request_form.html", {'form' : form_class})
I've tried using django-guardian and django-objectpermissions, but I've not seen the exact way of using them. I've even created a user-permission for it, but as the admin views and templates are not user-written code, I couldn't give a defnition.
base : It is a leave management system project, where a user can edit his type of leave even after submitting the form, thanks.
update -
How do I display particular fields in the html page.
from my models.py the 'accepted' field is a Boolean field, after the user submits the form I want to display the field's status , whether it is True or False in a html page. I'm unable to retrieve that field from the db..
What you can do is override the ModelAdmin readonly_fields method by making all other fields read only except the one the user should be able to update.
def get_readonly_fields(self, request, obj=None):
# the logged in user can be accessed through the request object
if obj and request.user.is_staff:
readonly_fields = [f.name for f in self.opts.fields]
readonly_fields.remove('type_leave_field')
return readonly_fields
Regarding the list comprehension:
self.opts gives you access to the Model specified in your Meta class (or #admin.register() should also be fine).
self.opts.fields is a tuple consisting of field objects from that model. Each object is a dictionary with meta data of a field. By accessing the .name (key) of each field object we can get the field name represented as a string.
Python :3.6.2 Django : 1.11.4
We are trying to use foreignkey across apps. Address is consumed by both customer and agent. We are also using inline formset. Form showing up correct. Please see below for the picture.
Create Agent Screen
However, when you save it, it saves just the just last of address. Also please note that in edit, a blank line is added which I think should not added by default. Please see below for the pic.
Edit Agent screen
Folder structure
apps (Folder)
agent (app)
models.py
views.py
customer (app)
models.py
views.py
sharedmodels(app)
models.py
forms.py
agent/views.py
from django.shortcuts import render
# Create your views here.
from django.db import transaction
from django.urls import reverse_lazy
from django.views.generic import CreateView, UpdateView, ListView
from apps.agent.models import Agent
from apps.sharedmodels.forms import AgentAddressFormSet
from multiApps.logger import log
class AgentList(ListView):
model = Agent
class AgentCreate(CreateView):
model = Agent
fields = ['first_name']
#log.info(fields)
class AgentAddrLocArefCreate(CreateView):
model = Agent
fields = ['first_name']
success_url = reverse_lazy('agent-list')
def get_context_data(self, **kwargs):
data = super(AgentAddrLocArefCreate, self).get_context_data(**kwargs)
log.info('i m here 1')
if self.request.POST:
log.info('i m here 2')
#data['location'] = LocationFormSet(self.request.POST)
data['address'] = AgentAddressFormSet(self.request.POST)
#data['agentreferal'] = AgentReferalFormSet(self.request.POST)
else:
#data['location'] = LocationFormSet()
data['address'] = AgentAddressFormSet()
#data['agentreferal'] = AgentReferalFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
#location = context['location']
agentaddress = context['address']
log.info('i m here 3')
#agentreferal = context['agentreferal']
with transaction.atomic():
self.object = form.save()
#if location.is_valid() and address.is_valid() and agentreferal.is_valid():
if agentaddress.is_valid():
#location.instance = self.object
agentaddress.instance = self.object
#agentreferal.instance = self.object
#location.save()
agentaddress.save()
#agentreferal.save()
return super(AgentAddrLocArefCreate, self).form_valid(form)
class AgentAddrLocArefUpdate(UpdateView):
model = Agent
fields = ['first_name']
success_url = reverse_lazy('agent-list')
# log.info('i m here 4')
def get_context_data(self, **kwargs):
data = super(AgentAddrLocArefUpdate, self).get_context_data(**kwargs)
log.info(data)
if self.request.POST:
log.info('i m here 5')
#data['location'] = LocationFormSet(self.request.POST, instance=self.object)
data['address'] = AgentAddressFormSet(self.request.POST, instance=self.object)
#data['agentreferal'] = AgentReferalFormSet(self.request.POST, instance=self.object)
else:
#data['location'] = LocationFormSet(instance=self.object)
data['address'] = AgentAddressFormSet(instance=self.object)
#data['agentreferal'] = AgentReferalFormSet(instance=self.object)
return data
def form_valid(self, form):
context = self.get_context_data()
#location = context['location']
agentaddress = context['address']
log.info('i m here 6')
#agentreferal = context['agentreferal']
with transaction.atomic():
self.object = form.save()
#if location.is_valid() and \
if agentaddress.is_valid():
#location.instance = self.object
agentaddress.instance = self.object
#agentreferal.instance = self.object
#location.save()
agentaddress.save()
#agentreferal.save()
return super(AgentAddrLocArefUpdate, self).form_valid(form)
def brokenview(request):
# first, intentionally log something
log.info('This is a manually logged INFO string.')
log.debug('This is a manually logged DEBUG string.')
# then have the view raise an exception (e.g. something went wrong)
raise Exception('This is an exception raised in a view.')
# return HttpResponse('hello')
sharedmodels/forms.py
from django.forms import ModelForm, inlineformset_factory
from apps.agent.models import Agent
from apps.customer.models import Customer
from .models import *
class AgentAddressForm(ModelForm):
class Meta:
model = Address
exclude = ('customer',)
class AddressForm1(ModelForm):
class Meta:
model = Address
exclude = ('agent',)
AgentAddressFormSet = inlineformset_factory(Agent, Address, form=AgentAddressForm, extra=1)
AddressFormSet1 = inlineformset_factory(Customer, Address, form=AddressForm1, extra=1)
#LocationFormSet = inlineformset_factory(Agent, Location, form=LocationForm, extra=1)
***One other point: We are successful in doing this within one app.
Any immediate help on this really appreciated.
I have a form in my application which has a hidden form field, the value of which I want to set in my corresponding view after submitting the form.
forms.py
class EvangelizedForm(forms.ModelForm):
first_name = forms.CharField(help_text="First Name")
last_name = forms.CharField(help_text="Last Name")
email = forms.CharField(help_text="Email ID")
mobile_no = forms.CharField(help_text="Mobile number")
twitter_url = forms.CharField(help_text="Twitter URL")
twitter_followers = forms.CharField(widget = forms.HiddenInput()) #Hidden form field
class Meta:
model = Evangelized
fields = ('first_name','last_name', 'twitter_url', 'email', 'mobile_no')
models.py
class Evangelized(models.Model):
first_name = models.CharField(max_length=128)
last_name = models.CharField(max_length=128)
email = models.EmailField()
mobile_no = models.CharField(unique=True, max_length = 10, validators=[RegexValidator(regex='^\w{10}$', message='Mobile number should be strictly of 10 digits.')])
twitter_url = models.CharField(unique=True, max_length=128)
twitter_followers = models.CharField(max_length = 128)
views.py
def fillform(request):
follower_count = '250'
if request.method == 'POST':
form = EvangelizedForm(request.POST)
if form.is_valid():
form.fields['twitter_followers'] = follower_count
form.save(commit=True)
return index(request)
else:
form.errors
else:
#form = EvangelizedForm()
if request.user.is_authenticated():
form = EvangelizedForm(initial={'first_name': request.user.first_name,
'twitter_url': 'https://twitter.com/' + request.user.username,
'last_name': request.user.last_name})
else:
form = EvangelizedForm()
context = RequestContext(request,
{'request': request,
'user': request.user, 'form':form})
#return render(request, 'rango/fillform.html', {'form': form, 'context_instance':context})
return render_to_response('rango/fillform.html',
context_instance=context)
Basically, I'm trying to set the value of twitter_followers (which is a hidden form field in forms.py) in my index view, by:
follower_count = '250'
..
..
form.fields['twitter_followers'] = follower_count
By doing this, I'm expecting the value of 'twitter_followers' in the database after submitting the form to be '250'. However, this approach doesn't seem to be working.
What's the right way to set values to certain attributes in the database manually using views?
You need to set it on the model instance, which is the result of form.save. That's the main reason for the commit argument in the first place.
if form.is_valid()
obj = form.save(commit=True)
obj.twitter_follower = follower_count
obj.save()
You can override the save method of the form, with something like this:
def save(self, *args, **kwargs)
twitter_followers = kwargs.pop('twitter_followers', 0)
self.instance.twitter_followers = twitter_followers
super(Evangelized, self).save(args, kwargs)
And then in the view just have to call in this way:
form.save(twitter_followers=250)