I am working on a Flask Application to do some event scheduling. I am having problems with WTForms QuerySelectMultipleField in my form.
forms.py
class EnterEvent(Form):
...
invitees = QuerySelectMultipleField('Invitees', query_factory=lambda:
models.User.query.order_by(models.User.name).all())
and in my init.py file where I parse the Form POST data. Just to test I tried to return request.form['invitees'] just to see what is passed. Eventually I want to validate the data and add it to my SQLite3 DB.
#app.route('/event', methods=['POST', 'GET'])
def addEvent():
form = EnterEvent()
if request.method == 'POST':
...
invitees = request.form['invitees']
return invitees
the WTForm docs say that the QuerySelectMultipleField should return a list with ORM model instances but when I parse the POST request I am not getting a list. I checked the POST data in my browser and it looks like when I select multiple objects it is sending more than one.
I can't seem to figure this out. Any help will be greatly appreciated!
You would get your ORM model instances if you query your form object directly, rather then the 'raw' form data that's part of the request object
Assuming you're using Flask-WTF with it's little helpers built it, your invitees line should really read invitees = form.invitees.data.
Related
I have a problem getting data from a form using it in a route
forms.py
class Calculator(Form):
amount = IntegerField('Amount')
weight = IntegerField('Weight')
class Program(Form):
cycles = IntegerField('Cycles')
volume = FormField(Calculator)
app.py
#app.route('/', methods=('GET', 'POST'))
def index():
form = forms.Program()
if form.validate_on_submit():
values = models.Progression(
cycles=form.cycles.data,
amount=form.amount.data,
weight=form.weight.data
)
return render_template('index.html', form=form, values=values)
The data for cycles comes through just fine but I am unsure of the syntax for how to access the encapsulated form within my route. The docs say FormField will return the data dict of the enclosed form but I can't seem to figure out how to grab that and put it in a variable.
I was able to grab the data I needed using
amount=form.volume.amount.data,
weight=form.volume.weight.data
The real issue came from the fact that the form was not validating when I was using FormField. A rookie error I should have checked earlier.
I had to enable CSRF protection by importing it from flask_wtf and using CsrfProtect(app)
The problem is that the form data does not come through as the attributes of the Calculator class. The data is sent as a dictionary from the volume attribute.
Test it out with: print form.volume.data
(I suggest commenting out your values object and just use print statements)
The output should be: {'amount': foo, 'weight': bar}
Thanks for teaching me something! I never knew of FormField.
I'm new in web dev and I'm trying for a project to develop a Restful web api and a website. I'm using Django framework and following tutos, I always see html files as static and the different views rendering an html template. This way of doing seems to me as backend and frontend are not much separated. Is it possible to have backend only developed in Django ?
edit:
I have actually a problem more specific. I having this app (records) with a view having "patient_list" using Response class from REST framework that renders some data and an html template like this:
def patient_list(request):
"""
List all records, or create a new .
"""
if request.method == 'GET':
#data = Patient.objects.all()
data= Patient.objects.all()
#serializer = PatientSerializer(data, many=True)
#return JSONResponse(serializer.data)
return Response({'patients': data}, template_name='records.html')
in my urls.py I have:
url(r'^records/$', views.patient_list),
and here I'm a little confused. Suppose one called this /records, so patient_list is called and will response with an html page. From what I understood (maybe wrong), a restful API should renders data in a standard view so that it can be used from any "frontend" (html pages or mobile app). Is this statement correct ? am I doing it wrong using Response with an html template ?
With Vanilla Django
Of course it's possible, and without any additional libraries to Django.
E.g. You can define a Django view that returns JSON data instead of rendering it into a HTML template server-side. Effectively making an API instead of a "website".
Here's an example with a class-based view that accepts GET requests and returns some JSON data with JsonResponse
from django.views.generic import View
from django.http import JsonResponse
class MyView(View):
def get(self, request):
my_data = {'something': 'some value'}
return JsonResponse(my_data, mimetype='application/json')
As you can see, you don't have to use the HTML rendering facilities in Django, they're there only if you want to use them.
With REST libraries
And of course there is a host of libraries to build RESTful APIs with Django, like Django REST Framework and Tastypie
Multiple representations and content negotiation
Content negotiation is the process of selecting one of multiple possible representations to return to a client, based on client or server preferences.
You can support more than one format in your REST API. E.g. you can support both HTML and JSON formats. There are various ways to do this:
You may use a GET param ?format=JSON (and have it default to HTML e.g.)
You may use Accept headers
You may have two URLs /records.html and /records.json (the format suffix method)
More on this topic in DRF's documentation
E.g. if you were to implement the first method with the GET params you could modify your code this way:
if request.method == 'GET':
data = Patient.objects.all()
format = request.GET.get('format', None)
if format == 'JSON':
serializer = PatientSerializer(data, many=True)
return JSONResponse(serializer.data)
else:
# Return HTML format by default
return Response({'patients': data}, template_name='records.html')
I am reading the Flask Web Development book and came across this:
def edit_profile():
form = EditProfileForm()
if form.validate_on_submit():
current_user.name = form.name.data
current_user.location = form.location.data
current_user.about_me = form.about_me.data
db.session.add(user)
flash('Your profile has been updated.')
return redirect(url_for('.user', username=current_user.username))
form.name.data = current_user.name
form.location.data = current_user.location
form.about_me.data = current_user.about_me
return render_template('edit_profile.html', form=form)
Basically, when the form is not posted or doesn't validate, this copies over the data from the current user. Now reading up on wtforms, I read this about the init method on a form:
obj – If formdata is empty or not provided, this object is checked for attributes
matching form field names, which will be used for field values.
So I guess that means that we could write this (the sample below is my own):
def edit_post(post_id):
post = Post.query.get_or_404(post_id)
if current_user != post.author:
abort(403)
# Below is the line I am concerned about
form = PostForm(formdata=request.form, obj=post)
if form.validate_on_submit():
form.populate_obj(post)
db.session.commit()
return redirect(url_for('user', username=current_user.username))
return render_template('post_form.html', form=form)
I figure that this should fill the form instance from the database model on GET, and from POST data after post. Testing this, it seems to work..
Now my question: is this way of writing an edit view correct? Or should I copy everything over field by field, like in the book?
Loading in a POST MultiDict is certainly the accepted way to map key/value pairs to your WTForms instance. Even more so, if you are using the Flask-WTF extension, this is automatically done for you, it is one of the perks that this extension brings you.
If you would crack open the code of Flask-WTF you would see that it inherits the SecureForm class of WTForms and tries to load in the Werkzeug POST MultiDict (called formdata) by default (if it is present). So loading in your form in your view like this:
form = PostForm(obj=post)
Should be sufficient (if using Flask-WTF) to also fill the fields with POSTed data.
The way it is done in your book example is certainly not wrong, but creates a lot of unnecessary code and is error prone / redundant - one could forget to mention a field in the view that is declared in the WTForms instance.
I have a translation engine api made using Django Python and it has a function that I want to be made into a class based view:
def i18n_newkey(rin):
request = Request(rin, JSONParser())
if request.method == 'POST':
data = json.loads(rin.body)
.... # Parsing and other code here and finally,
return JSONResponse(kdata)
Where the JSONResponse is an HttpResponse that renders it's content into JSON.
My url: url(r'^savekey', 'i18n_newkey'),
I'm new to django and I wanted to convert my function based view to class view, I've read the Django Documentation but I cannot seemed to dig it in this particular code.
Thanks!
Seems like JsonRequestResponseMixin will do exactly what you want.
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.