Deleting FlaskForm Fields depending on userinput in other forms - python

I have 3 forms with Checkboxes to configure the desired form (Final_Form). After the user chooses the desired fields (in form1, form2 and form3), i want to delet all fields that are not required in the final form and render the final form. The reason for that is, that i have 3 Subkategories with around 12 possible values, in each form (form1-form3) the user can choose one ore more subkategories. The subcategories are standardized and are used to describe a clinical incident. The users wished to have the subcategories (1-3; form1-form3) seperated and always with an example (right-side of the screen in an anther bootstrap col).
The finalform is than a combination of the the subcategories that matches best to describe the clinical incident. All fields in the Final_Form are TextAreaFields. The Input for the TextAreaFields is stored in a sqlite-db.
Here is how i tried it:
app.py:
if request.method == 'POST' and form1.form1Submit.data:
OnePointOne = form1.OnePointOne.data
if not OnePointOne:
del Final_Form.OnePointOne
return render_template('Form2.html', form2 = form2)
if request.method == 'POST' and form2.form2Submit.data:
TwoPointTwo = form2.TwoPointTwo.data
if not TwoPointTwo:
del Final_Form.TwoPointTwo
return render_template('Form3.html', form3 = form3)
if request.method == 'POST' and form3.form3Submit.data:
ThreePointThree = form3.ThreePointThree.data
if not ThreePointThree:
del Final_Form.ThreePointThree
return render_template('Final.html', Final_Form = Final_Form)
forms.py:
class form1(FlaskForm):
OnePointOne = BooleanField('Effect')
form1Submit = SubmitField('Submit Category')
class form2(FlaskForm):
TwoPointTwo = BooleanField('Measure')
form2Submit = SubmitField('Submit Category')
class form3(FlaskForm):
ThreePointThree = BooleanField('Result')
form3Submit = SubmitField('Submit Category')
class Final_Form(FlaskForm):
OnePointOne = TextAreaField('Example Effect')
TwoPointTwo = TextAreaField('Example Measure')
ThreePointThree = TextAreaField('Example Result')
Final_FormSubmit = SubmitField('Submit incident')
The problem is, that the formfields of the Final_Form objects dont get deleted (only inside the if statements). I am very thankful for every hint or explanation.

As you are showing three separate pages, there are three separate requests.
You Final_Form object cannot be simply kept between these requests.
I don't fully understand why you configure your third form this way, it would be helpful to explain your use-case for better advice.
Without more information, I'm thinking of some ways to do this:
You make it one page/request, where you go from form to form using AJAX.
You make it one page with all forms, controlling visualisation with JS + CSS
You save your desired value somewhere
probably you can keep it in cookie (session object)
or in database, if that makes sense in your context
Also, please include whole code of this function - it's not clear how you create those forms you use.

Related

Django How to add a model other than a form

I am making an app using Django2.0. But There are things that I do not know even if I investigate.
There are models as follows.
class Picture(models.Model):
place = models.ForeignKey(Place, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
image = models.ImageField(upload_to='image/')
using the following form
class PictureForm(forms.ModelForm):
class Meta:
model = Picture
fields = ['place', 'title', 'image']
And run following code on views.py
obj = Picture()
pic = PictureForm(request.POST, request.FILES, instance=obj)
pic.save()
I can add model peacefully. But in this way, I should select place and title on form every time. Beforehand I decide place and title, I will only select image on the form. It reduces my work.
Since there are several place and title, I do not want to set them to default values.
I succeeded in making a form.
class PictureForm(forms.ModelForm):
class Meta:
model = Picture
fields = ['image',]
But I don't know how to add title and place on views.py
I'm not good at English. Thank you for seeing the bad sentensecs to the end
Your issue comes from the view code, where you instantiate your new model.
If you want to create a new model you don't need to pass the insance argument, just edit the lines to get the following :
pic = PictureForm(request.POST, request.FILES)
pic.save()
See https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/#the-save-method
To be able to set only a few fields you should update your model declaration, with for example null=True, Blank=True, default=None in the optional fields arguments.
PS : the django documentation is transalated in many languages, but if you don't find yours try the IRC channels.
If I understand your problem clearly,
You have predecided the place and the title and want the user to enter only the image. Only image should be kept in the form, but you want to enter the title and the place in the view.If that's the case,do this :
Your view function:
def some_view(request):
if request.method == "POST":
T,P = get_title_and_place()
# This fuction will return a tuple with the title and the place that you need.
# Or Alternatively, you can pass the title and place as argument to the view
obj = Picture(title=T,place=P)
#Initialise object with title and place received from function or arguments
pic_form = PictureForm(request.POST, request.FILES)
#The form contains only the image field as in your last block of code
if pic_form.is_valid():
obj.image = request.FILES['image']
obj.save()
#Save the object
return redirect('some_url')
else:
return render('xyz.html',{"form":pic_form})
#Incoming image is invalid. Throw back the invalid form to the user
else:
#Incoming method is not POST, give user the form to fill the image
pic_form = PictureForm()
return render('xyz.html',{"form:pic_form})
On submission of the form from xyz.html, you should redirect back to this view. This is the reason why I made a seperate function to get the title and place so that user doesn't know about it via the url.
However be careful with this method. You have to take care of validating the title and place. Otherwise the obj.save() will throw error

What's the best way to link views and pass data between them?

I have a basic diary application that has two views: Add and Edit view. When user hits 'Add' button on Add view, the data is saved into database and simple message is shown. When user hits 'Update' button on Edit View, the data is updated and a last update stamp is added.
So far these views run independently. The Edit View by default loads the last saved entry and allows for that to be updated.
I want to update it so that upon successful addition of new diary entry on the Add View, it transitions to the Edit View to allow the user to Edit and Update if they want to. How do I link the views together and pass the relevant data to Edit View to know which Entry (database object) to fetch for edit? Also I would like to be able to use the Edit View independently to fetch a specified diary entry for example on a GET. So the Edit View is agnostic to whomever called it, it just knows which diary entry to load.
The code for Add and Edit View as they are now, are displayed below:
def addEntry(request):
entryForm = None
if request.method == "POST":
entryForm = EntryForm(request.POST)
if entryForm.is_valid():
entryForm.save(commit = True)
request.method = "GET"
return entrySubmitted("Entry has been submitted")
else:
logger.info(entryForm.errors)
else:
# Set up view with new entry form
entryForm = EntryForm()
template = getEntryViewTemplate(entryForm)
return render(request, "DiaryEntry.html", template)
def editEntry(request):
# Get the latest entry from the database
entry = Entry.objects.last();
if request.method == 'GET':
entryForm = EntryForm(instance = entry)
else:
entryForm = EntryForm(request.POST, instance = entry)
if entryForm.is_valid():
entryForm.setLastUpdated(datetime.now())
entryForm.save(commit = True)
templateData = getEntryViewTemplate(entryForm)
return render(request, "EditEntry.html", templateData)
Thanks in Advance,
Francis
Look django documentation about Class Base View.
https://docs.djangoproject.com/en/1.10/topics/class-based-views/intro/#
You can create a class, with different method.
Get, Post(create object), Put(Update object)
And you have a lot of methods and attributes usefull.
You can link your class view to a specific model (Entry) for example.
https://docs.djangoproject.com/fr/1.10/ref/class-based-views/mixins-single-object/#django.views.generic.detail.SingleObjectMixin.get_queryset
How do I link the views together and pass the relevant data to Edit View to know which Entry (database object) to fetch for edit?
You need to pass ID Object to edit. Two solutions. In your Post data. Or by argument in your url.
http://your_web_site.com/entry/id_entry/
And you init your form with entry data
In your URL django you can link to url to your class base view.
url(r'^entry/$', views.EntryView.as_view(), name='create_entry'),
url(r'^entry/(?P<pk>[-\w/]+)$', views.EntryView.as_view(), name='update_entry'),
I hope this will help you

Django: How do I access post data and match with a criteria

I need some guidance on best practice implementation of the following.
I have a scenario where I am building an app, but if it matches a certain "category" or "locale" and want to redirect it to a page in between else just go the normal route.
Here is my simple views.py
if form.is_valid():
...
kwargs = {'project_id':project_id, 'categories':request.POST['categories'], 'locale':request.POST['locale']}
process_se(request, **kwargs)
return HttpResponseRedirect(obj.next_url)
Here is what I have in my models.py file but it seems to be very inconsistent.
Is there a better way to handle this request?
def process_se(self, request, **kwargs):
if "All" or "Sweden" in kwargs['locale']:
if "Technology" or "Internet" in kwargs['categories']:
next_url = request.build_absolute_uri(reverse('project_new_se', kwargs={'project_id': self.id}))
else:
next_url = request.build_absolute_uri(reverse('project_new_step2', kwargs={'project_id': self.id}))
self.next_url = next_url
UPDATES:
I am using forms.ModelForm, categories and locales are ManyToManyField's
I have simulated a for in the shell and still seem to get no result
Here is the cleaned_data output
f.cleaned_data
{'locale': [<Locale: Sweden>, <Locale: All>], 'categories': [<Category: Technology>, <Category: Internet>]}
Although running this for fields in the form seem to render perfectly fine based on your solution
I originally proposed putting this code in the form class, but ApPeL revised the question to point out that locale and categories are many-to-many fields on the model. So now I suggest putting a method like this in your model:
def requires_swedish_setup(self):
"""
Return True if this project requires extra Swedish setup.
"""
return (self.locale.filter(name__in = ('All', 'Sweden')).exists())
and self.categories.filter(name__in = ('Technology', 'Internet')).exists())
and then implementing your view like this:
if form.is_valid():
project = form.save()
next = 'project_new_step2'
if project.requires_swedish_setup():
next = 'project_new_se'
next_url = reverse(next, kwargs={'project_id': project.id})
return HttpResponseRedirect(next_url)
Some notes:
I'm assuming that Locale and Category objects have name fields (if not, use whatever field contains the name you are testing).
It's not a good idea to read form data out of request.POST (widgets haven't had a chance to run, and it hasn't been validated): it's better to use form.cleaned_data.
You don't need to call request.build_absolute_uri in this case: it's fine to feed the result of reverse directly to HttpResponseRedirect.
"All" or "Sweden" in kwargs['locale'] is probably not what you mean: it parses like "All" or ("Sweden" in kwargs['locale']) and so is always true.

Multiple Instances of a django app, does django support this

I have written a simple feedback application in django. It's not particulairly complex, basically it allows authenticated users to write a shot message with a subject line and submit that message via a form. I then allows who are in a selected group to view user submitted feedback. In the future I may add more functionality but for now it does what I want.
Here comes my question, the site I'm building has multiple places where I would like to use the feedback app, for example I have a "what do you think of the site?" kind of page at /dev/feedback/ I also have one for customer support feedback at "/support/feedback/" Currently I have just copied the code from my mysite.apps.dev.feedback over to mysite.apps.support.feedback.
The problem is that this has now created two separate copies of the same code. Despite having just written the app the two versions are already starting to diverge which is annoying. My question is simply how do I create multiple instances of the same app in a django site with distinct database models?
Some resources I've found related but not helpful are https://docs.djangoproject.com/en/dev/topics/http/urls/ and Reversing namespaced URLs in Django: multiple instances of the same app The first page does not offer much on the issue and the second page provides somewhat cludgey and impractical solutions that seem to be both unrelated and more work than their worth. Is there a proper way to implement multiple instances of the same django app?
Single model approach
I'd personally try to keep this as one app and have a view that can handle being posted from multiple locations / tag them appropriately.
As S.Lott says, this is the way to go. I am providing alternatives if you're curious about methods to keep your code in one place in other situations.
For example, you could add a category field to your model, set up a single url conf which accepts an argument in the URL such as /(?P<category>\w+/feedback/$ and have the view simply tag the feedback with the appropriate category.
class MyForm(forms.ModelForm):
class Meta:
model = Feedback
def my_view(request, category):
form = MyForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
feedback = form.save(commit=False)
feedback.category = category
feedback.save()
return http.HttpResponse("Thanks for posting!")
return render(request, "mytemplate.html", {'form': form})
# urls.py
(r'^(?P<category>\w+)/feedback/$', 'my_view')
# user can visit dev/feedback or support/feedback and the feedback will be tagged appropriately
Abstract base class
Another solution is to build an abstract base class, then create subclasses for your distinct tables. That should solve the issue with your code getting out of sync.
You'd have a single abstract model (which has no tables) from which your "real" models in your separate apps would be based on.
Dynamically generated views
If you must have separate models, you could potentially write a dynamically constructed view.
def view_generator(model_class):
class MyForm(forms.ModelForm):
class Meta:
model = model_class
def my_view(request):
form = MyForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
form.save()
return http.HttpResponse("Thanks for posting!")
return render(request, "mytemplate.html", {'form': form})
return my_view
# urls.py
from foo import view_generator
(r'^my_first_feedback_form', view_generator(Model1))
(r'^my_second_feedback_form', view_generator(Model2l))
how do I create multiple instances of the same app in a django site with distinct database models?
You shouldn't.
You simply use the feedback app model in the other two apps with a simple from feedback.models import Feedback.
Then your support app can create, retrieve, update and delete Feedback objects.
Your dev app, similarly, can create, retrieve, update and delete Feedback objects because it imported the model.
That's all that's required: import.
Thanks Yuji Tomita for a very thorough answer, my final solution is derived very closely from his suggestion, but is different enough that I thought I would post it as another option if someone else runs into the same situation that I am in.
Firstly in my mysite.apps.feedback.models file I put
class Feedback( models.Model ):
subject = models.TextField( max_length=100 )
body = models.TextField( max_length=100 )
# Some other stuff here...
# Finally I used the suggestion above and created a field which I
# use to label each entry as belonging to a specific instance of the app.
instance_name = models.TextField( max_length=20 )
In my mysite.apps.feedback.views file I put
def save_message( request, instance_name ):
if request.method == 'POST':
form = FeedbackFrom( request.POST )
if form.is_valid():
form.instance.instance_name = instance_name
form.save()
return render("feedback/thanks.html")
else:
return render("feedback/submit.html", {'form':form })
else:
return render("feedback/submit.html",{'form':FeedbackForm()})
#user_passes_test( is_staff )
def all_messages( request, instance_name ):
messages = Feedback.objects.filter( instance_name = instance_name )
return render("feedback/view_all.html",{'feedback':messages} )
In my mysite.apps.dev.urls file I put
url(r'^feedback/', include('mysite.apps.feedback.urls'),
{'instance_name':'dev'}),
In my mysite.apps.support.urls file I put
url(r'^feedback/', include('mysite.apps.feedback.urls'),
{'instance_name':'support'}),
This will separate feedback messages by app instance. Note that my actual code is more complex but this should be good enough for anyone with a similar problem to get a solution up and running pretty quickly. Hope this is useful to anyone in a similar situation. Thanks again to Yuji Tomita for the suggestions upon which this solution is based.

django ModelForm save() method issue

I have a model form:
class SnippetForm(ModelForm):
class Meta:
model = Snippet
exclude = ['author', 'slug']
and I want to be able to edit a particular instance by using this:
def edit_snippet(request, snippet_id):
#look up for that snippet
snippet = get_object_or_404(Snippet, pk=snippet_id)
if request.user.id != snippet.author.id:
return HttpResponseForbidden()
if request.method == 'POST':
form = SnippetForm(data=request.POST, instance=snippet)
if form.is_valid():
form.save()
return HttpResponseRedirect(snippet.get_absolute_url())
else:
form = SnippetForm(instance=snippet)
return render_to_response(SNIPPET_EDIT_TEMPLATE,
{'form':form, 'add':False, 'user':request.user},
RequestContext(request))
Notice that at the line
form = SnippetForm(data=request.POST, instance=snippet)
, I created a form that use the data supplied from the user, and bound it with the instance found using the primary key (received from the url). According to django documentation, when I call save() the existing instance should be updated with POSTED data. Instead, what I see is a new object is created and saved into the database. What went wrong? Thanks a lot.
[Edit] This is really embarrassed. The code indeed has nothing wrong with it. The only thing that messed up the whole thing was the action I put in the template (as I use a same template for add and edit a snippet)....Thanks a lot for your help, really appreciate that.
I don't see why it would happen. What version of django is it?
In any case, you can manually force update passing the corresponding argument.
form = SnippetForm(data=request.POST, instance=snippet, force_update=True)

Categories