Check if user voted and if not save vote - django

In a simple survey app users should vote only once on a set of questions (survey). I'm able to check if the user voted (either lazy user or authorized user), but only if I prepopulate the fields with test data in the database. I'm also aware that a lazy user could just delete the cookie and would be able to vote again.
I'm not sure how to call save() after form.is_valid(), because I can't call it twice.
models.py
class Vote(models.Model):
user = models.ForeignKey(User)
survey = models.ForeignKey(Survey)
vote = models.BooleanField(default=False) # not in use yet
def __unicode__(self):
return self.vote
views.py
#allow_lazy_user
def survey_detail(request, slug, template_name='survey.html'):
# allows only active surveys to be called otherwise displays HTTP 404
survey = get_object_or_404(Survey, is_active=True, slug=slug)
# checks for user id's in current survey
voter = [
v.user.id for v in Vote.objects.filter(survey=survey)
]
# checks if user already voted on survey
if request.user.id in voter:
# TODO: use different template or redirect
return render(request, 'index.html')
form = ResponseForm(request.POST or None, survey=survey)
if form.is_valid():
response = form.save(commit=False)
# gets the current user (lazy or not)
response.user = request.user
response.save()
# HOW DO I SAVE VOTER HERE?
return HttpResponseRedirect(reverse('survey:confirm', args=(slug,)))
data = {
'response_form': form,
'survey': survey
}
return render(request, template_name, data)
Another thought I have is to use the boolean field vote in the model and check for True or False in the template.

Okay, I solved it with
#allow_lazy_user
def survey_detail(request, slug, template_name='survey.html'):
[...]
form = ResponseForm(request.POST or None, survey=survey)
if form.is_valid():
response = form.save(commit=False)
response.user = request.user
response.save()
if not request.user.id in voter:
vote = Vote(user=request.user, survey=survey, vote=True)
vote.save()
return HttpResponseRedirect(reverse('survey:confirm', args=(slug,)))
[...]
I'm sure there are better ways, but it works for now.

New solution, since survey_id and user_id are already in model Response:
#allow_lazy_user
def survey_detail(request, slug, template_name='itbarometer/survey_detail.html'):
# allows only active surveys to be called otherwise displays HTTP 404
survey = get_object_or_404(Survey, is_active=True, slug=slug)
categories = [c.name for c in Category.objects.filter(survey=survey)]
voter = [v.user.id for v in Response.objects.filter(survey=survey)]
# checks if survey_id and user_id are already linked
# if yes, redirect
if request.user.id in voter:
return redirect('bereits-abgeschlossen/')
form = ResponseForm(request.POST or None, survey=survey)
if form.is_valid():
response = form.save(commit=False)
# gets the current user (lazy or not)
response.user = request.user
response.save()
return HttpResponseRedirect(reverse('survey:confirm', args=(slug,)))
data = {
'response_form': form,
'survey': survey,
'categories': categories
}
return render(request, template_name, data)

Related

Setting user kwargs as field vales in Django Form with Class-based View

I have a Django application (1.11) to track referrals (referred by a user). I want to pass the id of the authenticated user to the ModelForm 'referrer' field (and since it's from the current logged in user the field shouldn't be editable).
class Referral(models.Model):
name = models.CharField(max_length=100)
referrer = models.ForeignKey('users.User', on_delete=models.SET_NULL, related_name='referrals', null=True, blank=True)
View:
class ReferralFormView(FormView):
form_class = ReferralForm
template_name = "refer.html"
success_url = reverse_lazy('thanks')
def get(self, request):
if request.user.is_authenticated():
return super(ReferralFormView, self).get(request)
else:
return redirect('login')
def get_form_kwargs(self):
user = self.request.user
form_kwargs = super(ReferralFormView, self).get_form_kwargs()
form_kwargs['referrer'] = user.id
return form_kwargs
def form_valid(self,form):
...
form.save()
return super(ReferralFormView, self).form_valid(form)
I override get_form_kwargs in the view, then modify form init
class ReferralForm(forms.ModelForm):
class Meta:
model = Referral
def __init__(self, *args, **kwargs):
referrer = kwargs.pop('referrer', None)
super(ReferralForm, self).__init__(*args, **kwargs)
self.fields['referrer'].disabled = True
self.fields['referrer'].queryset = User.objects.filter(id=referrer)
However all I see is a blank referrer field, what am I missing to make the user the value of that field (which can't be edited)? I also tried self.fields['referrer'].initial = User.objects.filter(id=referrer). I don't want the user to have to select their own username from a queryset of one.
I can print a <QuerySet [<User: username>]> for user = User.objects.filter(id=referrer), so why isn't it setting that user as the field value?
Update: I can assign the user value with
self.fields['referrer'].initial = User.objects.filter(id=referrer).first()
self.fields['referrer'].disabled = True
However, on form submit Referral obj is not saving with the referrer field value (that value's still blank)
thanks
what I needed to do was select the user obj in the queryset:
self.fields['referrer'].initial = User.objects.filter(id=referrer).first()
but using self.fields['referrer'].disabled = True disabled the field so I was still getting a blank value on submit (disabled doesn't do what the docs say it does). Using self.fields['referrer'].initial = User.objects.filter(id=referrer).first() with that field set as readonly allows the form to submit with the initial value

2 Forms on same model not saving as same user - Django

I'm creating a questionnaire / survey, and have two forms (Model Form) built on the same model. These forms are called on separate views, but when saved they appear as separate users in the database. I'm not sure how to get them so save as the same user, I am already using the ' post = form.save(commit=False), post.user = request.user, post.save()' method to save the forms.
EDIT: Added in an attempt to save to the same instance
Model:
class QuizTakers(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
industry_choices = (
(1, 'Service'),
(2, 'Hospitality'),
(3, 'Wholesale/Retail'),
(4, 'Manufacturing'),
(5, 'Agriculture')
)
industry = MultiSelectField(choices=industry_choices, max_length=1, max_choices=1)
company_name = models.CharField( max_length=100)
email = models.EmailField(blank=True)
score = models.FloatField(default=0)
completed = models.BooleanField(default=False)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.company_name
Forms:
# Form for getting company name
class QuizTakerForm(forms.ModelForm):
class Meta:
model = QuizTakers
fields = ['company_name']
# Form for getting company industry
class QTIndustryForm(forms.ModelForm):
class Meta:
model = QuizTakers
fields = ['industry']
Views:
# view for getting company name
def start(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = QuizTakerForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
request.session['company_name'] = form.cleaned_data['company_name']
post = form.save(commit=False)
post.user = request.user
post.save()
# redirect to a new URL:
return HttpResponseRedirect('industry/')
# if a GET (or any other method) we'll create a blank form
else:
form = QuizTakerForm()
return render(request, 'ImpactCheck/start.html', {'form': form})
# view for getting industry
class IndustryView(FormView):
template_name = 'ImpactCheck/industry.html'
form_class = QTIndustryForm
success_url = '1/'
def get(self, request):
company_name = request.session['company_name']
this_user=QuizTakers.objects.filter(company_name=company_name).order_by('-timestamp').first()
form=self.form_class(instance=this_user)
company_name = request.session['company_name']
return render(request, 'ImpactCheck/industry.html', {'form': form, 'company_name': company_name})
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
post = form.save(commit=False)
post.user = self.request.user
post.save()
return HttpResponseRedirect('/1')
Firstly, in your def start(request) function, you should consider adding the ID to request.session instead of the company name. Something along the lines of
def start(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = QuizTakerForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
form.instance.user=request.user
form.save()
request.session['obj_id'] = post.id
# redirect to a new URL:
return HttpResponseRedirect('industry/')
Now you can use that id to get both the name of your company, as well as the object.
In your IndustryView(FormView), if you're having trouble with the form instances, it's better to use UpdateView instead of the FormView (Be sure to import UpdateView first)
class IndustryView(UpdateView):
template_name = 'ImpactCheck/industry.html'
model = QuizTakers
fields = ['industry']
success_url = '/1'
def get_object(self):
return QuizTakers.objects.get(pk=self.request.session.get('obj_id'))
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['company_name'] = QuizTakers.objects.get(pk=self.request.session.get('obj_id'))
return ctx
We use the get_context_data method since you need the company_name in your template. The get_object method in this view, tells django which object is to be updated. By default, it grabs the pk from the url (as a url parameter). But since we store our id in the session, we need to explicitly define this function.
Also, since we switched to UpdateView, you no longer need the QTIndustryForm either.

Django Send Form By Authenticated User

i stuck when trying to send data or save data with django form by user it self (logged).
When i test why form "From" user must be selectable not automatic selected by user it self.
class ValidationCreate(forms.ModelForm):
class Meta:
model = About
fields = '__all__'
def upload(request):
upload = ValidationCreate()
if request.method == 'POST':
upload = ValidationCreate(request.POST, request.FILES)
if upload.is_valid():
upload.save()
return redirect('validation')
else:
return HttpResponse("""your form is wrong, reload on reload""")
else:
return render(request, 'upload_form.html', {'about_form': upload})
sample
this way you can assign the request.user
if upload.is_valid():
instance = upload.save(commit=False)
instance.profile = Profile.objects.get(user=request.user) # you can change the user if field name is different
instance.save()
return redirect('validation')
else:
in forms
fields = ['field_1', 'field_2',] # except user field

django forms - how to filter number of available options in a form

I'm trying to limit number of "categories" that user have available when entering new "feed" only to categories that he owns and he created. The way it works now is that user can add "feed" to other users' "categories" as this is what the form displays. How can I fix it ?
thanks!
-M
models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=50)
user = models.ForeignKey(User)
class Feed(models.Model):
url = models.URLField()
name = models.CharField(max_length=50)
created = models.DateTimeField(auto_now_add=True)
description = models.TextField(blank=True)
category = models.ForeignKey(Category)
user = models.ForeignKey(User)
forms.py
class FeedForm(forms.ModelForm):
class Meta:
model = Feed
exclude = ['user']
views.py
def addfeed(request, user):
user = request.user
page_title = "Add feed"
instance = Category.objects.filter(user=request.user)
if request.method == 'POST':
form = FeedForm(request.POST, instance=instance)
if form.is_valid():
feed = form.save(commit=False)
feed.user = request.user
feed.save()
return HttpResponseRedirect("/user/" + user.username + "/manage")
else:
form = FeedForm()
return render(request, "form_manage.html", {
'page_title': page_title,
'form': form,
})
Set the queryset attribute of the field somewhere. Because it depends on your user, it's something you have to set during or after instantiating the form. For instance, here's how to do it in the view:
def addfeed(request, user):
user = request.user # why does this view take user as an arg and then reassign?
page_title = "Add feed"
categories = Category.objects.filter(user=request.user)
if request.method == 'POST':
form = FeedForm(request.POST)
form.fields['category'].queryset = categories
if form.is_valid():
feed = form.save(commit=False)
feed.user = request.user
feed.save()
return HttpResponseRedirect("/user/" + user.username + "/manage")
else:
form = FeedForm()
form.fields['category'].queryset = categories
return render(request, "form_manage.html", {
'page_title': page_title,
'form': form,})
I removed the instance argument to your POST case's form construction because that's meant for passing in an existing Feed instance, not a categories queryset.
You could also do this in the form's __init__ if you pass in the correct categories queryset.
I use javascript to do this. For example, you could pass a list of the relevant categories as extra context in your view then use javascript in your template to empty the pre-populated option field in the form and replace it with your extra context.

Set form field value before is_valid()

I'm having a bit of trouble grasping how to do this. I've put my best effort into searching Google without any luck.
I'll start with a bit of code and explain what I'm trying to do as I go:
models.py
class Action(models.Model):
title = models.CharField(max_length=200)
owner = models.ForeignKey(User, related_name='actions')
created_by = models.ForeignKey(User, related_name='+', editable=False)
modified_by = models.ForeignKey(User, related_name='+', editable=False)
class ActionForm(ModelForm):
class Meta:
model = Action
views.py
By default, there is a dropdown field for owner. I have an icon that allows the user to enter a new username in a text field instead for owner. I check to see if owner_new was submitted and if so, create that user. I then need to set the owner field to that value so that form.is_valid() will be true.
def action_create(request):
if request.method == 'POST':
form = ActionForm(request.POST)
# check if new user should be created
if 'owner_new' in request.POST:
# check if user already exists
user = User.objects.get(username=request.POST.get('owner_new'))
if not user:
user = User.objects.create_user(request.POST.get('owner_new'))
# HERE IS WHERE I'M STUMPED
form.owner = user.id
if form.is_valid(): # THIS FAILS BECAUSE form.owner ISN'T SET
action = form.save(commit=False)
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
else:
form = ActionForm()
return render(request, 'actions/create.html', {'form': form})
You can try this:
def action_create(request):
if request.method == 'POST':
form = ActionForm(request.POST)
# check if new user should be created
if 'owner_new' in request.POST:
# check if user already exists
user, _ = User.objects.get_or_create(username=request.POST.get('owner_new'))
updated_data = request.POST.copy()
updated_data.update({'owner': user})
form = MyForm(data=updated_data)
if form.is_valid(): # THIS FAILS BECAUSE form.owner ISN'T SET
action = form.save(commit=False)
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
else:
form = ActionForm()
return render(request, 'actions/create.html', {'form': form})
A cleaner way of doing this is:
add required=False to the owner field.
Now,
if form.is_valid(): # THIS DOES NOT FAIL EVEN IF form.owner ISN'T SET
action = form.save(commit=False)
if 'owner_new' in request.POST:
user, _ = User.objects.get_or_create(username=request.POST.get('owner_new'))
action.owner = user
action.created_by = request.user
action.modified_by = request.user
action.save()
return redirect('action_register:index')
I came into a similar situation and couldn't figure out how to do it the way I wanted. What I ended up with was putting a link to a UserForm which allows a user to create a new owner, and then redirect back to the ActionForm with the argument initial={owner: new_owner} included when instantiating the form.