Using Django 1.5.5, Django-CMS 2.4.2
I wrote a plugin for django-cms *(cms.plugin_base.CMSPluginBase)*. Plugin create a some form, that works fine. But I get a problem - after submitting a form - how can I set a cookie ?
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from poll.models import PagePoll
from poll.forms import PollForm
from django.http import HttpResponse
class PollPlugin(CMSPluginBase):
""" upload a poll """
model = PagePoll
name = u'Poll'
render_template = u"plugins/page_poll.html"
def render(self, context, instance, placeholder):
#upload poll form
request = context['request']
form = PollForm(instance.poll, request.POST)
#validate the form
if request.method == 'POST':
if form.is_valid():
form.save()
#=SAVE COOKIES HERE=
else:
form = PollForm(instance.poll)
context['form'] = form
return context
plugin_pool.register_plugin(PollPlugin)
The answer was found here:
https://djangosnippets.org/snippets/2541/
The problem was that cms-plugin can't return a responce object, where cookies need to bet set... So the trick is to throw a custom exception with HttpResponceRedirect
Related
I am working on a flask form which has an image upload field, when I submit the form the data attribute of this FileField is set to None. As per the flask documentation.
The FileField provided by Flask-WTF differs from the WTForms-provided
field. It will check that the file is a non-empty instance of
FileStorage, otherwise data will be None.
So I checked in my code for this isinstance(form.profile_picture, FileStorage) and this returns false.
Below are the relevant code snips
Forms.py
from flask_wtf import FlaskForm
from wtforms import PasswordField, StringField, SubmitField, TextAreaField
from wtforms.validators import DataRequired, Email, EqualTo, length
from flask_wtf.file import FileField, FileAllowed
class EditProfileForm(FlaskForm):
username = StringField('Username', validators=[DataRequired()])
about_me = TextAreaField('About Me', validators=[DataRequired(),
length(min=10, max=1000)])
profile_picture = FileField('Profile Picture', validators=[FileAllowed([
'jpg', 'png'])])
update = SubmitField('Update')
views.py
#user.route("/edit_profile/", methods=['GET', 'POST'])
def edit_profile():
form = EditProfileForm()
if form.validate_on_submit():
user = User.query.filter_by(username=current_user.username).first()
user.username = form.username.data
user.about_me = form.about_me.data
# if form.profile_picture.data: # this returns None
# user.profile_pic = process_image(form.profile_picture.data,
# user.username)
print(isinstance(form.profile_picture, FileStorage))
db.session.commit()
return redirect(url_for("user.profile", uname=current_user.username))
elif request.method == 'GET':
form.username.data = current_user.username
form.about_me.data = current_user.about_me
return render_template('user/user_edit_profile.html', form=form)
So am I missing something here ? I have also gone through this question Flask-WTF FileField does not set data attribute to an instance of Werkzeug FileStorage but this was not the issue in my case, as I have not initialized the form with any data.
The solution to this was quite simple, I didn't mentioned the correct encoding type in my form in the template file.
This fixed my problem.
<form method='POST' action='' enctype=multipart/form-data>
I want to count how may files the user has uploaded.
I have added signals.py
from django.dispatch import Signal
upload_completed = Signal(providing_args=['upload'])
And summary.py
from django.dispatch import receiver
from .signals import upload_completed
#receiver(charge_completed)
def increment_total_uploads(sender, total, **kwargs):
total_u += total
to my project.
My views upload
#login_required
def upload(request):
# Handle file upload
user = request.user
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Document(docfile=request.FILES['docfile'])
newdoc.uploaded_by = request.user.profile
upload_completed.send(sender=self.__class__, 'upload')
#send signal to summary
newdoc.save()
# Redirect to the document list after POST
return HttpResponseRedirect(reverse('upload'))
else:
form = DocumentForm() # A empty, unbound form
# Load documents for the upload page
documents = Document.objects.all()
# Render list page with the documents and the form
return render(request,'upload.html',{'documents': documents, 'form': form})
This effort does not work.I got
upload_completed.send(sender=self.__class__, 'upload')
^
SyntaxError: positional argument follows keyword argument
I found signal example testing-django-signals
from .signals import charge_completed
#classmethod
def process_charge(cls, total):
# Process charge…
if success:
charge_completed.send_robust(
sender=cls,
total=total,
)
But it seems to me that classmethod would not work in my case
How to fix my method?
You don't need the 'uploads' argument for the send() method.
But a tip, if you're planning on a persistent count of the number of file uploads (which I assume you most likely are), then I think you should create a new model so that you can save it in your database.Then you can update that model every time a Document model is saved.
And I suggest you have a look at post_save. Have a nice day coding!
I can't seem to be able to mock the behaviour of a form when unit testing views.
My form is a simple ModelForm and resides in profiles.forms. The view is (again) a simple view that checks whether the form is valid and then redirects.
views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse
from profiles.forms import ProfileForm
def home(request):
form = ProfileForm()
if request.method == 'POST':
form = ProfileForm(request.POST)
if form.is_valid():
profile = form.save()
return HttpResponseRedirect(reverse("thanks"))
My test looks like this:
class TestViewHomePost(TestCase):
def setUp(self):
self.factory = RequestFactory()
def test_form(self):
with mock.patch('profiles.views.ProfileForm') as mock_profile_form:
mock_profile_form.is_valid.return_value = True
request = self.factory.post(reverse("home"), data={})
response = home(request)
logger.debug(mock_profile_form.is_valid.call_count) # "0"
is_valid is not being called on the mock, which means ProfileForm is not patched.
Where did I make a mistake?
I was able to fix mocking is_valid as following:
def test_form(self):
with mock.patch('profiles.views.ProfileForm.is_valid') as mock_profile_form:
mock_profile_form.return_value = True
request = self.factory.post(reverse("home"), data={})
response = home(request)
note: and you could use mock_profile_form.assert_called_once() to check if the mock has been called.
I have a requirement here to build a comment-like app in my django project, the app has a view to receive a submitted form process it and return the errors to where ever it came from. I finally managed to get it to work, but I have doubt for the way am using it might be wrong since am passing the entire validated form in the session.
below is the code
comment/templatetags/comment.py
#register.inclusion_tag('comment/form.html', takes_context=True)
def comment_form(context, model, object_id, next):
"""
comment_form()
is responsible for rendering the comment form
"""
# clear sessions from variable incase it was found
content_type = ContentType.objects.get_for_model(model)
try:
request = context['request']
if request.session.get('comment_form', False):
form = CommentForm(request.session['comment_form'])
form.fields['content_type'].initial = 15
form.fields['object_id'].initial = 2
form.fields['next'].initial = next
else:
form = CommentForm(initial={
'content_type' : content_type.id,
'object_id' : object_id,
'next' : next
})
except Exception as e:
logging.error(str(e))
form = None
return {
'form' : form
}
comment/view.py
def save_comment(request):
"""
save_comment:
"""
if request.method == 'POST':
# clear sessions from variable incase it was found
if request.session.get('comment_form', False):
del request.session['comment_form']
form = CommentForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
if request.user.is_authenticated():
obj.created_by = request.user
obj.save()
messages.info(request, _('Your comment has been posted.'))
return redirect(form.data.get('next'))
else:
request.session['comment_form'] = request.POST
return redirect(form.data.get('next'))
else:
raise Http404
the usage is by loading the template tag and firing
{% comment_form article article.id article.get_absolute_url %}
my doubt is if am doing the correct approach or not by passing the validated form to the session. Would that be a problem? security risk? performance issues?
Please advise
Update
In response to Pol question. The reason why I went with this approach is because comment form is handled in a separate app. In my scenario, I render objects such as article and all I do is invoke the templatetag to render the form. What would be an alternative approach for my case?
You also shared with me the django comment app, which am aware of but the client am working with requires a lot of complex work to be done in the comment app thats why am working on a new one.
I dont see the problem with security, except situation when you using cookies for stroring session. The performance depends on what kind of session backand you are using as well. But I cant find the point why are you complicating things!
And I dont thing that touching session in template tag is a good idea at all.
And maybe Take a look at django Comments Framework
Update:
Ok. I cant see the problems in this approach except complication. For example in my project, i'm using ajax to send data and validate it right in the comments view, therefore I do not require to redirect to original page. Other thing is that I pass the initialized Form in article view, so i'm not using templatetags.
Can provide you with my approche for example purposes:
from forms import CommentForm
from models import Comment
from django.http import HttpResponseForbidden, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils import simplejson
from etv_persons.person.models import Person
from django.contrib import messages
def create_comment(request,slug):
if request.method != 'POST' or not request.POST or request.user.is_anonymous():
return HttpResponseForbidden('Доступ запрещен')
person = get_object_or_404(Person,slug=slug)
form = CommentForm(data=request.POST)
if form.is_valid():
Comment.objects.create(user_id=request.user.id, person=person,text=form.cleaned_data['text'])
if request.is_ajax():
msg={'msg': 'Cement was send',}
else:
messages.info(request, 'COmment was send.')
else:
if request.is_ajax(): msg={'msg': 'Error.',}
else: messages.info(request, 'Error.')
if request.is_ajax():
return HttpResponse(simplejson.dumps(msg),content_type='application/json')
else:
return redirect('person_details',**{"slug":slug,"ptype":person.type})
And in the article view we just do:
response['comment_form'] = CommentForm()
And yes, I do not validate the comments form. There is no reason. Just one text input.
i've a probelem with the request.user.is_authenticated()
this the view.
from django.http import HttpResponseRedirect
from django.contrib.auth.models import User
from django.shortcuts import render_to_response
from django.template import RequestContext
from forms import RegistrationForm
def ContributorRegistration(request):
if request.user.is_authenticated():
'''if user is logged in -> show profile'''
return HttpResponseRedirect('/profile/')
if request.method == 'POST':
'''if post, check the data'''
form = ContributorRegistration(request.POST)
if form.is_valid():
''' if form is valid, save the data'''
user = User.objects.create_user(username=form.cleaned_data['username'],email = form.cleaned_data['email'], password= form.cleaned_data['password'])
user.save()
contributor = user.get_profile()
contributor.location = form.cleaned_data['location']
contributor.save()
return HttpResponseRedirect('profile.html')
else:
'''form not valid-> errors'''
return render_to_response('register.html',{'form':form},context_instance=RequestContext(request))
else:
'''method is not a post and user is not logged, show the registration form'''
form = RegistrationForm()
context={'form':form}
return render_to_response('register.html',context,context_instance=RequestContext(request))
basically,
if the user is logged in then the profile.html is shown: OK
if the user is not logged in and he's not posting data then the form is shown: OK
when i submit the data from the form i receive back this error:
Request Method: POST
Request URL: http://localhost:8000/register/
Django Version: 1.4.1
Exception Type: AttributeError
Exception Value:
'QueryDict' object has no attribute 'user'
Exception Location: /Users/me/sw/DjangoProjects/earth/views.py in ContributorRegistration, line 9
where line 9 is if request.user.is_authenticated():
so seems that the request does not have the user object when submitting the form data.
how can i solve?
Thanks
You're filling your own view function with the request.POST data as if it was the form.
if request.method == 'POST':
'''if post, check the data'''
form = ContributorRegistration(request.POST)
if form.is_valid():
Should be
if request.method == 'POST':
'''if post, check the data'''
form = RegistrationForm(request.POST)
if form.is_valid():
In order to have access to request.user object, you need to have the User authentication middleware installed in your application. To do this (extremely easy), do the following:
Go to your settings.py and add 'django.contrib.auth' and 'django.contrib.contenttypes' to the INSTALLED_APPS tupple.
You are very likely to require a syncdb command for it to be fully installed (you need some database tables for user authentication).
python manage.py syncdb
And that should make it work.
Is it just me or is the name of your form the same as your view function ContributorRegistration?
Perhaps you've made a typo.