I am a newbie to Django and this question might be an easy one, but it has already been bothering me for a while.
<form method="post" action="/vote/" class="vote_form">
<input type="hidden" id="id_link" name="user_profile" class="hidden_id" value="5" />
<input type="hidden" id="id_voter" name="voter" class="hidden_id" value="3" />
New Score: <input name="score" class="" value="7" />
<button type="submit">Submit</button>
</form>
urls.py
url(r'^vote/$', auth(VoteFormView.as_view()), name="vote"),
views.py
class VoteFormView(FormView):
form_class = VoteForm
def create_response(self, vdict=dict(), valid_form=True):
response = HttpResponse(json.dumps(vdict))
response.status = 200 if valid_form else 500
return response
def form_valid(self, form):
profile = get_object_or_404(UserProfile, pk=form.data['user_profile'])
user = self.request.user
score = form.cleaned_data['score']
prev_votes = Vote.objects.filter(voter=user, profile=profile)
has_voted = (len(prev_votes) > 0)
ret = {"success": 1, "profile": profile, "voter: ": user}
if not has_voted:
# add vote
v = Vote.objects.create(voter=user, profile=profile, score=score)
ret["voteobj"] = v.id
# else response already ranked
return self.create_response(ret, True)
def form_invalid(self, form):
...do something when form invalid...
forms.py
class VoteForm(forms.ModelForm):
class Meta:
model = Vote
models.py
class Vote(models.Model):
voter = models.ForeignKey(User)
profile = models.ForeignKey(UserProfile)
score = models.FloatField(default=10.0)
def __unicode__(self):
return "%s voted %s" % (self.voter.username, self.profile)
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
# Extra attributes
As you can see, I have a simple form with 2 fields: a user_profile (PK of a user profile) and a score. After the form is submitted, it has this error: "form_errors": {"profile": ["This field is required."]} . So it always goes to to form_invalid.
Where am I supposed to get profile object if it is not form_valid?
I tried to redefine VoteForm as below, which does not work either.
class VoteForm(forms.ModelForm):
def clean_profile(self):
profile_pk = self.cleaned_data['profile']
profile = None
try:
profile = UserProfile.objects.get(pk=profile_pk)
except UserProfile.DoesNotExist:
raise forms.ValidationError("User profile doesn't exist.")
return profile
def save(self, *args, **kwargs):
vote = super(VoteForm, self).save(*args, **kwargs)
return vote
class Meta:
model = Vote
Any ideas?
You have to change your hidden input name from user_profile to profile.
Keep in mind this is a poor design since anyone can inject an arbitrary voter id.
Here is how I would do it
class VoteForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user")
super(VoteForm, self).__init__(*args, **kwargs)
def save(self, *args, **kwargs):
# manipulate self.user
and then in your view
form = VoteForm(..., user=request.user)
The name of your hidden field is user_profile, but your code refers to it as just profile. Try changing the html of the form so it is named profile.
As an aside you should include the voter id in your form, that should be added in your processing code based on the logged in user. Currently anyone could change the voter hidden field and record a vote for a different user.
Related
I am new to django, and I am creating a vacation application. I want to be able to when I create a new trip, the user that created the trip becomes a member of that trip.
here is my models.py file:
class Trip(models.Model):
trip_name = models.CharField(max_length=255,unique=False)
start_date = models.DateField(default=datetime.date.today)
end_date = models.DateField(default=datetime.date.today)
slug = models.SlugField(allow_unicode=True,unique=True)
members = models.ManyToManyField(User,through='TripMember')
def __str__(self):
return self.trip_name
def save(self,*args,**kwargs):
self.slug = slugify(self.trip_name)
super().save(*args,**kwargs)
def get_absolute_url(self):
return reverse('trips:single',kwargs={'slug':self.slug})
class Meta:
ordering = ['start_date']
class TripMember(models.Model):
trip = models.ForeignKey(Trip,null=True,related_name='memberships',on_delete=models.SET_NULL)
user = models.ForeignKey(User,null=True,related_name='user_trips',on_delete=models.SET_NULL)
def __str__(self):
return self.user.username
class Meta:
unique_together = ('trip','user')
this is my forms.py file:
class TripCreateForm(forms.ModelForm):
class Meta:
fields = ('trip_name','start_date','end_date')
model = Trip
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["trip_name"].label = "Trip Name"
self.fields["start_date"].label = "Start Date"
self.fields["end_date"].label = "End Date"
here is my views.py file:
class CreateTrip(CreateView):
form_class = TripCreateForm
template_name = 'trips/trip_form.html'
and my trip_form.html page:
<form action="{% url 'trips:create' %}" method="post" id='tripForm'>
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-primary btn-large" value="Create">
</form>
Where would I put the code to set the user as a tripmember and why?Also, if there is a better way to have set this up please let me know! I was gonna put it in the save part of the model but I am not quite sure if that is correct. Thanks!
You can override the form_valid() method of the CreateTrip class in your view:
def form_valid(self, form):
"""If the form is valid, save the associated model."""
self.object = form.save()
# add the current user to the members list of the trip
user = self.request.user
self.object.members.add(user)
return super().form_valid(form)
When submitting the button after clicking like it is creating the object successufuly but like field is updating with null value and django is getting error 302. Can somebody please help me with this.
After Edit
I have removed form_valid function from the SongVoteUpdateView now update part is working fine but if I removed form_valid function from SongVoteCreatView It is throwing intigrity error like this NOT NULL constraint failed: album_vote.song_id. For trace back please refer this link, if not removed it will creat blank object with no like.
models.py
Codes in models.py
class VoteManager(models.Manager):
def get_vote_or_unsaved_blank_vote(self,song,user):
try:
return Vote.objects.get(song=song,user=user)
except ObjectDoesNotExist:
return Vote(song=song,user=user)
class Vote(models.Model):
UP = 1
DOWN = -1
VALUE_CHOICE = ((UP, "👍️"),(DOWN, "👎️"),)
like = models.SmallIntegerField(null=True, blank=True, choices=VALUE_CHOICE)
user = models.ForeignKey(User,on_delete=models.CASCADE)
song = models.ForeignKey(Song, on_delete=models.CASCADE)
voted_on = models.DateTimeField(auto_now=True)
objects = VoteManager()
class Meta:
unique_together = ('user', 'song')
views.py
Codes in views.py
class SongDetailView(DetailView):
model = Song
template_name = 'song/song_detail.html'
def get_context_data(self,**kwargs):
ctx = super().get_context_data(**kwargs)
if self.request.user.is_authenticated:
vote = Vote.objects.get_vote_or_unsaved_blank_vote(song=self.object, user = self.request.user)
if vote.id:
vote_url = reverse('music:song_vote_update', kwargs={'song_id':vote.song.id,'pk':vote.id}) #'pk':vote.id
else:
vote_url = reverse('music:song_vote_create', kwargs={'song_id':vote.song.id})
vote_form = SongVoteForm(instance=vote)
ctx['vote_form'] = vote_form
ctx['vote_url'] = vote_url
return ctx
class SongVoteCreateView(CreateView):
form_class = SongVoteForm
model = Vote
def get_success_url(self,**kwargs):
song_id = self.kwargs.get('song_id')
return reverse('music:song_detail', kwargs={'pk':song_id})
def form_valid(self, form):
user = self.request.user
song_obj = Song.objects.get(pk=self.kwargs['song_id'])
vote_obj, created = Vote.objects.get_or_create(song = song_obj, user = user)
form.instance = vote_obj
return super(SongVoteCreateView).form_valid(form)
class SongUpdateVoteView(UpdateView):
form_class = SongVoteForm
model = Vote
# def form_valid(self, form):
# user = self.request.user
# song_obj = Song.objects.get(pk=self.kwargs['song_id'])
# vote_obj, created = Vote.objects.get_or_create(song = song_obj, user = user)
# form.instance = vote_obj
# print(form)
# return super().form_valid(form)
def get_success_url(self):
song_id = self.kwargs.get('song_id')
return reverse('music:song_detail', kwargs={'pk':song_id})
urls.py
url mapping
path('album/song/<int:pk>/',views.SongDetailView.as_view(), name='song_detail'),
path('album/song/create/<int:song_id>/',views.SongVoteCreateView.as_view(), name='song_vote_create'),
path('album/song/update/<int:song_id>/<int:pk>/', views.SongUpdateView.as_view(), name='song_vote_update')
song_detail.html
Codes in html page
<div>
<form action="{{vote_url}}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ vote_form.as_p }}
<button class="btn btn-primary" type="submit" >Vote</button>
</form>
</div>
forms.py
Code in forms.py
class SongVoteForm(forms.ModelForm):
like = forms.ChoiceField(widget=forms.RadioSelect,choices=Vote.VALUE_CHOICE)
class Meta:
model = Vote
fields = ['like',]
error Code
response got in console.
[31/Oct/2019 04:15:19] "GET /album/song/1/ HTTP/1.1" 200 3560
**[31/Oct/2019 04:15:21] "POST /album/song/update/1/8/ HTTP/1.1" 302 0**
[31/Oct/2019 04:15:21] "GET /album/song/1/ HTTP/1.1" 200 3560
I have explicitly mentioned like in the form_valid in SongVoteCreateView as shown below. Now the form is not creating blank object and redirecting to the correct page.
def form_valid(self, form):
like = form.cleaned_data.get('like')
user = self.request.user
song_obj = Song.objects.get(pk=self.kwargs['song_id'])
vote_obj, created = Vote.objects.get_or_create(song = song_obj, user = user, like=like)
form.instance = vote_obj
return super().form_valid(form)
I have a little problem. I want to to display in list only records belongs to user whos add it. In my app when I'am login as 'user' and I want to add new records incude records from list, I see all records in db from ForeignKey. How to make it correctly?
In 'Strona www' when I expand the list I see all records, not only records added by user personally.
My view for it is:
#login_required
def new_keyword(request):
if request.method == "POST":
new_keyword = KeywordForm(request.POST)
if new_keyword.is_valid():
new_keyword=new_keyword.save(commit=False)
new_keyword.user = request.user
new_keyword.save()
messages.success(request, 'Pomyślnie dodano słowo kluczowe')
return render(request, 'konto/new_keyword_ok.html')
else:
new_keyword = WebsiteForm()
return render(request, 'konto/new_keyword.html', {'new_keyword':new_keyword})
in forms.py I have:
class KeywordForm(forms.ModelForm):
class Meta:
model = Keyword
fields = ('www', 'keyword')
models.py
class Keyword(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Użytkownik")
www = models.ForeignKey(Website, on_delete=models.CASCADE, verbose_name="Strona www")
keyword = models.CharField(max_length=250, verbose_name="Słowo kluczowe", unique=False)
urls.py
path('new-keyword/', new_keyword, name='new_keyword'),
and html for display the form is:
{% if request.user.is_authenticated %}
<form action="." method="post">
{{ new_keyword.as_p }}
{% csrf_token %}
<p><input type="submit" value="Dodaj nowe słowo kluczowe" ></p>
Powrót do monitoringu
</form>
{% endif %}
EDIT:
models.py
from django.db import models
from django.contrib.auth.models import User
class Website(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Użytkownik")
website = models.CharField(max_length=250,verbose_name='Strona www', unique=False)
class Meta:
verbose_name = 'Strona www'
verbose_name_plural = 'Strony www'
def __str__(self):
return self.website
class Keyword(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="Użytkownik")
www = models.ForeignKey(Website, on_delete=models.CASCADE, verbose_name="Strona www")
keyword = models.CharField(max_length=250, verbose_name="Słowo kluczowe", unique=False)
class Meta:
verbose_name = 'Słowo kluczowe'
verbose_name_plural = 'Słowa kluczowe'
def __str__(self):
return self.keyword
Pass the request.user to your form and use the inverse relation user.website_set :
forms :
class KeywordForm(forms.ModelForm):
class Meta:
model = Keyword
fields = ('www', 'keyword')
def __init__(self, user, *args, **kwargs):
super(KeywordForm, self).__init__(*args, **kwargs)
self.fields['www'].queryset = user.website_set.all()
self.user = user
def save(self, commit=True):
instance = super(KeywordForm, self).save(commit=False)
instance.user = self.user
if commit:
instance.save()
return instance
views:
#login_required
def new_keyword(request):
if request.method == "POST":
new_keyword = KeywordForm(request.user, request.POST)
if new_keyword.is_valid():
new_keyword.save()
messages.success(request, 'Pomyślnie dodano słowo kluczowe')
# DONT DO THIS ! REDIRECT INSTEAD
return render(request, 'konto/new_keyword_ok.html')
else:
new_keyword = KeywordForm(request.user)
return render(request, 'konto/new_keyword.html', {'new_keyword':new_keyword})
As a side note: after a successful POST you want to redirect (even if to the same url). This avoids a lot of troubles (and duplicate submissions) when a user reloads the page
Just pass the user id to your form and then set a queryset for your field
like this:
How to pass the user to your form:
in your view:
#login_required
def new_keyword(request):
if request.method == "POST":
#### Notice this part.
new_keyword = KeywordForm(data=request.POST, u=request.user)
if new_keyword.is_valid():
new_keyword=new_keyword.save(commit=False)
new_keyword.user = request.user
new_keyword.save()
messages.success(request, 'Pomyślnie dodano słowo kluczowe')
return render(request, 'konto/new_keyword_ok.html')
else:
# Notice this part.
new_keyword = WebsiteForm(data=None, u=request.user)
return render(request, 'konto/new_keyword.html', {'new_keyword':new_keyword})
How to retrieve the user in form and set the queryset:
forms.py:
class KeywordForm(forms.ModelForm):
class Meta:
model = Keyword
fields = ('www', 'keyword')
def __init__(self, current_user, *args, **kwargs):
self.u = kwargs.pop("u") ## The user you just passed.
super(KeywordForm, self).__init__(*args, **kwargs)
self.fields['www'].queryset = Website.objects.filter(user=self.u) ## Setting the queryset.
I'm trying to make this work, with no success so far.
I'm using Django 1.6.5 and mongoengine 0.8.7.
I have some ReferenceField field types in my models.py, which I need to show in a Choice Field type, into a forms.py file.
So, these choices must be filled with the queryset from the referenced (or related models). So when I for example choose to register something in my app, I can relate for example 'users' to 'brands' or 'causes', etc...
Anyways, this was a brief explanation of what I'm trying to achieve with my app, here's my models.py:
from mongoengine import *
class Brand(DynamicDocument):
name = StringField(min_length=3,max_length=10,unique=True)
admins = ListField(ReferenceField("Peer", dbref=True))
campaigns = ListField(ReferenceField("Campaign"))
peers_partner = ListField(ReferenceField("Peer"))
payments = ListField(ReferenceField("Payment_Campaign"))
medias = ListField(EmbeddedDocumentField("Media"))
description = StringField(min_length=10,max_length=500)
socials = ListField(DictField())
def __unicode__(self):
return self.name
#property
def pic_profile(self):
for x in self.medias:
if x.tag == "profile":
return x.url
#property
def pic_banner(self):
for x in self.medias:
if x.tag == "banner":
return x.url
#property
def video_profile(self):
for x in self.medias:
if x.tag == "video":
return x.url
As you can see, there are some ReferenceField fields on my class, these are relationships with other models, but I'm still stuck trying to populate a choice field from forms.py with these references, here's my forms.py:
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Fieldset, ButtonHolder, Submit, Field, Div
from bson.objectid import ObjectId
from mongoengine.queryset import Q
from mongoengine import *
from hdb.brand.models import *
class Form_save_brand(forms.Form):
name = forms.CharField()
admins = forms.ModelChoiceField(queryset=Brand.objects.get_or_create(id=Peer),empty_label="")
campaigns = forms.ChoiceField()
peers_partner = forms.ChoiceField()
payments = forms.ChoiceField()
medias = forms.ChoiceField()
socials = forms.ChoiceField()
def __init__(self, *args, **kwargs):
self.instance = kwargs.pop('instance', None)
super(Form_save_brand, self).__init__(*args, **kwargs)
if self.instance:
self.fields['name'].initial = self.instance.name
self.fields['admins'].initial = self.instance.admins
self.fields['campaigns'].initial = self.instance.campaigns
self.fields['peers_partner'].initial = self.instance.peers_partner
self.fields['payments'].initial = self.instance.payments
self.fields['medias'].initial = self.instance.medias
self.fields['socials'].initial = self.instance.socials
def save(self, commit=True):
brand = self.instance if self.instance else Brand()
brand.name = self.cleaned_data['name']
brand.admins = self.cleaned_data['admins']
brand.campaigns = self['campaigns']
brand.peers_partner = self.cleaned_data['peers_partner']
brand.payments = self.cleaned_data['payments']
brand.medias = self.cleaned_data['medias']
brand.socials = self.cleaned_data['socials']
if commit:
brand.save()
return brand
The only field where I'm doing tests it's the fisrt ReferenceField (admins), which is where I'm stuck, if I manage to solve this I can continue with the other ones.
This is the traceback from Django:
NameError at /brand/nuevo/
name 'Peer' is not defined
Request Method: GET
Request URL: http://localhost:9000/brand/nuevo/
Django Version: 1.6.5
Exception Type: NameError
Exception Value:
name 'Peer' is not defined
Exception Location: /home/kkoci/hipeers/hweb/hweb/hweb/brand/forms.py in Form_save_brand, line 11
Python Executable: /home/kkoci/hipeers/hweb/hipeersweb/bin/python
As you can see right now the query it's like (id=Peer) but I've tried with (id=field), (id=name), etc... with no luck.
I hope I've explained myself...
Any ideas?
Any help would be greatly appreciated, please I'm stuck with this.
Thanks in advance!
EDIT
This is how I initiliaze the form in views.py:
class AddBrand(CreateView):
model = Brand
form_class = Form_save_brand
def get_template_names(self):
return ["brand/brand_nuevo.html"]
def get_success_url(self):
return reverse('list')
def brand_valid(self, form):
self.object = form.save(commit=False)
self.object.user = self.request.user
messages.success(self.request, "The brand has been created.")
return super(AddBrand, self).brand_valid(form)
Then in template brand/brand_nuevo.html:
{% extends "base.html" %}
{% block body %}
<form action="." method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit"/>
</form>
{% endblock body %}
So, what you need is to instantiate the Form_save_brand with a specific peer so you can work out the dependencies and what is shown. This can be achieved by this (most of the code is kept the same)
You have to change the form to the following:
class Form_save_brand(forms.Form):
name = forms.CharField()
campaigns = forms.ChoiceField()
peers_partner = forms.ChoiceField()
payments = forms.ChoiceField()
medias = forms.ChoiceField()
socials = forms.ChoiceField()
def __init__(self, peer, *args, **kwargs):
# we pass a peer parameter that will be used in the queryset query
self.instance = kwargs.pop('instance', None)
self.fields['admins'] = forms.ModelChoiceField(queryset=Brand.objects.get_or_create(id=peer),empty_label="")
and then, on your view
class AddBrand(CreateView):
model = Brand
# you don't need a form class here
def get_form(self, form_class=None):
return Form_save_brand(<you set the peer value here>, **self.get-form_kwargs())
I'm having trouble using the UpdateView for a view consisting of a form and formset.
I have the following models: Item and Picture.
Picture is defined as:
class Picture(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, blank=False)
content_type = models.ForeignKey(ContentType, verbose_name="content type",
related_name="content_type_set_for_%(class)s")
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey("content_type", "object_id")
I have several models that contain pictures. For example, in the Item model:
class Item(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=255, blank=False)
pictures = generic.GenericRelation(Picture)
I have the following ItemCreateForm:
class ItemCreateForm(ModelForm):
def __init__(self, *args, **kwargs):
super(ItemCreateForm, self).__init__(*args, **kwargs)
class Meta:
model = Item
The PictureForm:
class PictureForm(forms.ModelForm):
id = forms.IntegerField(widget=forms.HiddenInput)
def __init__(self, *args, **kwargs):
super(PictureForm, self).__init__(*args, **kwargs)
def save(self):
data = self.cleaned_data
obj = Picture(**data);
# do something to obj
# obj.save()
class Meta:
model = Picture
fields = ['id', 'name']
And the view:
class ItemUpdateView(UpdateView):
form_class = ItemCreateForm
template_name = 'item/new.html'
model = Item
success_url = '/items/'
def get_context_data(self, **kwargs):
context = super(ItemUpdateView, self).get_context_data(**kwargs)
item = context['object']
# Dont' create any extra forms when showing an update view
PictureFormSet = formset_factory(PictureForm, extra=0)
return {'form': kwargs['form'],
'picture_formset': UploadFormSet(initial = [ model_to_dict(a) for pic in item.pictures.all()])}
def post(self, request, *args, **kwargs):
self.object = self.get_object()
item_form = ItemCreateForm(request.POST, instance=self.object)
if item_form.is_valid():
item = item_form.save(commit=False)
item.save()
# How do update the pictures?
This is my urls.py:
url(r'^items/(?P<pk>\d+)/update/$', ItemUpdateView.as_view(), name='item_update')
The template:
<form action="" method="post" enctype="multipart/form-data">
{% for field in form %}
# do something
{% endfor %}
{{ picture_formset.management_form }}
{% for form in picture_formset.forms %}
# do something
{% endfor %}
<input name="commit" type="submit" value="Submit" />
</form>
I'm new to Django.
The user can dynamically(via jQuery) add/remove pictures through the Picture form in the single template that is used to display the item and multiple pictures.
1 I had to include the id as a hidden field for the picture, otherwise the pictures will be inserted instead of an Update. QN: Is there a better way to do this?
2 How do I update the picture model? Currently request.POST doesn't have all the fields in the model, thus the model is complaining of NULL fields? I'm totally at lost how to deal with formset in an UpdateView and is not the main form, like a simple example of UpdateView with the pk in the url.
PictureFormSet = formset_factory(PictureForm)
picture_formset = PictureFormSet(request.POST, request.FILES)
for picture_form in picture_formset.forms:
picture_form.save()