I am new to Django and struggling with a basic problem, yet cannot find a solution online.
I have these models:
class Suggestion(models.Model):
author = models.ForeignKey('auth.User')
title = models.CharField(max_length=200)
description = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
class Vote(models.Model):
suggestion = models.ForeignKey(Suggestion)
voter = models.ForeignKey('auth.User')
vote_count = models.IntegerField(default=1)
and I'm trying create a view that would add a Vote to a given Suggestion, capturing the User who voted. I've seen some seem to do this with a form or with a regular function, so not sure what's the best practice here?
EDIT
Here is my attempt (not working). Any help/advice appreciated.
#forms.py
class VoteForm(forms.ModelForm):
class Meta:
model = Vote
fields = ()
#models.py
class Vote(models.Model):
suggestion = models.ForeignKey(Suggestion)
voter = models.ForeignKey('auth.User')
vote_count = models.BooleanField()
#views.py
def add_vote(request, pk):
if request.method == 'POST':
form = VoteForm(request.POST)
suggestion = get_object_or_404(Suggestion, pk=pk)
if form.is_valid():
vote = form.save(commit=False)
vote.voter = request.user
vote.vote_count = True
vote.save()
return render(request, 'suggestion/suggestion_detail.html', {'suggestion': suggestion})
#vote_form.html
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit">Vote</button>
</form>
You should create a form for the vote and include it on the Suggestion view. The form can have it's own html -- vote_form.html. Then include it on the suggestion html page with
{% include '[name of directory]/vote_form.html' %}
As for the vote count, it shouldn't be an integer field unless you want users to cast multiple votes. If you just want someone to be able to vote once per suggestion, you should make the vote_count a boolean field (either true or false). Then you can assign true to a vote and false to a non-vote.
I've managed to do what I wanted in this way:
#vote_form.html
<form action="{% url 'add_vote' suggestion.id %}" method="post">
{% csrf_token %}
<input type="submit" value="I want to vote">
</form>
#urls.py
urlpatterns = [
url(r'^suggestion/(?P<pk>\d+)/$', views.SuggestionDetail.as_view(), name="suggestion_detail"),
url(r'^suggestion/(?P<pk>\d+)/vote/$', views.add_vote, name='add_vote'),
]
#models.py
class Vote(models.Model):
suggestion = models.ForeignKey(Suggestion)
voter = models.ForeignKey('auth.User')
vote_count = models.BooleanField()
#views.py
def add_vote(request, pk):
suggestion = get_object_or_404(Suggestion, pk=pk)
vote = Vote(
suggestion = suggestion,
voter = request.user,
vote_count = True)
has_user_voted = Vote.objects.filter(voter=request.user, suggestion=suggestion).count()
if has_user_voted < 1:
vote.save()
else:
messages.error(request, 'It seems you have already voted, only one vote is allowed')
return HttpResponseRedirect(reverse('suggestion_detail', args=(suggestion.id,)))
Related
Help me please I don't know what's going on.
I wrote some simple blog, where I could add posts and comments. It was working well.
views.py:
def add_comment_to_post(request, pk):
post = get_object_or_404(Post, pk=pk) # calls the given model and get the object, type = <class 'blog.models.Post'
if request.method == "POST": # if we posted data
form = CommentForm(request.POST)
if form.is_valid(): # if all fields are filled
comment = form.save(commit=False) # create instance and return which not saved in database "Comment" <class Comment>
comment.post = post # return Title
comment.author = request.user
comment.save()
return redirect('post_detail', pk=post.pk)
else:
form = CommentForm() # type = <class 'blog.forms.CommentForm'>
return render(request, 'blog/add_comment_to_post.html', {'form': form})
But when I added a function for deleting comments, that wrote me a mistake, like this:
null value in column "approved_comment" of relation "blog_comment"
violates not-null constraint
views.py:
def comment_remove(request, pk):
post = get_object_or_404(Post, pk=pk)
comment = get_object_or_404(Comment, pk=pk)
print(comment)
comment.delete()
return redirect('post_detail', pk=post.pk)
I think I deleted comment incorrectly. I don't understand how primary keys works, how comments and posts are related each other. And I don't understand how to understand it.
ps.
models.py:
class Post(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL,
related_name="author_name",
on_delete=models.CASCADE)
title = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
published_date = models.DateTimeField(blank=True, null=True)
def publish(self):
self.published_date = timezone.now()
self.save()
def __str__(self):
return self.title
def approved_comments(self):
return self.comments.filter(approved_comment=True)
class Meta:
ordering = ['-published_date']
class Comment(models.Model):
post = models.ForeignKey('blog.Post', on_delete=models.CASCADE,
related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False),
# parent = models.ForeignKey('self', null=True, blank=True,
related_name='replies')
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return 'Comment {} by {}'.format(self.text, self.author)
pps. forms.py:
from xml.etree.ElementTree import Comment
from django import forms
from .models import Post, Comment
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ('title', 'text',)
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('text',)
To understand how to delete comments we need to understand how pk(primary keys) and related_name works.
Let's see model class Comment. Pay attention for post and related_name='comments' and than see it in post_detail.html.
class Comment(models.Model):
post = models.ForeignKey('blog.Post', on_delete=models.CASCADE,
related_name='comments')
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
post_detail.html:
<a class="btn btn-default" href="{% url 'add_comment_to_post' pk=post.pk %}">Add comment</a>
{% for comment in post.comments.all %}
<div class="comment">
<a class="btn btn-default" href="{% url 'comment_remove' pk=comment.pk %}"><span class="glyphicon glyphicon-remove"></span></a>
<div class="date">{{ comment.created_date }}</div>
<strong>{{ comment.author }}</strong>
<p>{{ comment.text|linebreaks }}</p>
</div>
{% empty %}
<p>No comments here yet :(</p>
{% endfor %}
post.comments.all - post is relating for model our Post model. The related_name specifies the name of the relation in reverse, so in this case accessing the Comment of a given Post object. [https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name]
Now you know how we get comment.pk.
After that please pay attention how we're removing comment.
views.py:
def comment_remove(request, pk):
comment = get_object_or_404(Comment, pk=pk)
comment.delete()
return redirect('post_detail', pk=comment.post.pk)
Look at the comment.post.pk. In our Comment model we have key post = models.ForeignKey('blog.Post', on_delete=models.CASCADE, related_name='comments'), which is relating with our Post Model. That's it!
Account is my AUTH_USER_MODEL and AccountDisplayInfo consists of all the additional display info of every account. So they can input and submit, and subsequently update their information. These are my codes, but I'm unsure why it isn't working. First of all, I am receiving this error:
DoesNotExist at /account/5/displayinfo/ AccountDisplayInfo matching query does not exist.
Secondly, the "update" function isn't working.
models.py
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
class AccountDisplayInfo(models.Model):
account = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
instagram = models.CharField(max_length=50, unique=True, blank=True, null=True) #instagram
.html
<form method="POST" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<div class="d-flex justify-content-center">
<button type="submit" class="btn btn-primary btn-sm col-lg-5">Update</button>
</div>
</form>
views.py
def display_information_view(request, *args, **kwargs):
user_id = kwargs.get("user_id")
account = Account.objects.get(pk=user_id)
context = {}
displayinfo = AccountDisplayInfo.objects.get(account=account)
if request.POST:
form = DisplayInformationForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
info = form.save(commit=False)
info.account = request.user
info.save()
messages.success(request, 'Your profile display information have been updated', extra_tags='editdisplayinfo')
return redirect("account:view", user_id=account.pk)
else:
form = DisplayInformationForm(request.POST, instance=request.user,
initial={
"instagram": displayinfo.instagram,
}
)
context['form'] = form
else:
form = DisplayInformationForm(
initial={
"instagram": displayinfo.instagram,
}
)
context['form'] = form
return render(request, "account/displayinfo.html", context)
forms.py
class DisplayInformationForm(forms.ModelForm):
class Meta:
model = AccountDisplayInfo
fields = ('instagram')
Also, would be great if you can advise on this::
If I have 2 tables. Table 1 and Table 2. Table 2 has a foreign key to table 1 but table 1 dont have a foreign key to table 2. How can I query table 2's data from table 1? Thanks
By default .get() will return a DoesNotExist exception if no object matches the query you executed and stop the code from running, so if you want to input it manually on the same page use filter instead:
displayinfo = AccountDisplayInfo.objects.filter(account=account).first()
Then in your template do something like this:
{% if displayinfo %}
... show display info...
{% else %}
<p> No info yet </p> <!-- (or show some form) -->
{% endif %}
To answer your other question:
You have to use the related_name or related models attribute to access the ForeignKey data or use the model name with the _set suffix, for example:
class Post(models.Model):
title = models.CharField(max_lenght=10)
class Comment(models.Model):
body = models.CharField(max_lenght=200)
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
then you would get the Post and its comments:
post = Post.objects.get(pk=1)
comments = post.comments.all()
if you didn't have the related_name attribute in your model field you would do this instead:
comments = post.comment_set.all()
UPDATE
Maybe the issue is in your Form class, try removing the save method from it and instead do this in your view:
if request.POST:
form = DisplayInformationForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
info = form.save(commit=False)
info.account = request.user
messages.success(request, 'Your profile display information have been updated', extra_tags='editdisplayinfo')
info.save()
return redirect("account:view", user_id=account.pk)
How to fix This Error I'm Trying To Fix This Error But I Get Again And Again
i want to detect user who fill the form for example test fill the form but when i write the code down below i get this error
Any Help Will Be Appreciated!
ERROR
user_register_model matching query does not exist.
ERROR SCREENSHOT
Here is my Views.py
def buy_form(request):
if request.method == 'POST':
usr_buy = user_buy_form(request.POST)
if usr_buy.is_valid():
usr_buys = usr_buy.save(commit=False)
user_register_obj = user_register_model.objects.get(user=request.user)
usr_buys.users = user_register_obj
usr_buys.save()
else:
return print(usr_buy.errors)
else:
usr_buy = user_buy_form()
context = {'usr_buy':usr_buy}
return render(request,'user_buy.html',context)
Here is my Models.py
class user_register_model(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
join_date = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.user.username
class user_buy(models.Model):
users = models.ForeignKey(User,on_delete=models.CASCADE)
title = models.CharField(max_length=200)
payment_method = models.CharField(max_length=500)
price = models.IntegerField()
Trade_limits = models.IntegerField()
Location = models.CharField(max_length=1000)
def __str__(self):
return self.users.user.username
Here is my Forms.py
class user_buy_form(forms.ModelForm):
class Meta():
model = user_buy
fields = '__all__'
exclude = ('users',)
Here is my user_buy.html
{% extends 'base.html' %}
{% block body_block %}
<form class="form-control" method="POST">
{% csrf_token %}
{{usr_buy.as_p}}
<input type="submit" class="btn btn-primary" value="Submit">
</form>
{% endblock %}
I didn't see any points here to create the user_register_model.If you are trying to add the currently logged in user you can do this:
request.user will give you the currently logged in user so for this the user must be logged in.
#login_required
def buy_form(request):
if request.method == 'POST':
usr_buy = user_buy_form(request.POST)
if usr_buy.is_valid():
usr_buys = usr_buy.save(commit=False)
usr_buys.users = request.user
usr_buys.save()
return redirect('some_path') # redirect to some path after saving the form
Class names should normally use the CapWords convention.
I think the request.user is not present in the user_register_model model thats why it is giving matching query doesnot exist error, first create it in the user_register_model and then query it.
I am trying to save simple form add_product, I don't have any error but the new product doesn't appear in admin or on a page. I really not sure what I am doing wrong. Any suggestion would be great! Thank you.
my views.py
def add_product(request):
author = request.user
product_form = ProductForm(request.POST, request.FILES, instance=author)
if product_form.is_valid():
form = product_form.save(commit=False)
form.save()
return HttpResponseRedirect('/products/')
else:
product_form = ProductForm()
return render(request, 'products/add_product.html', {'product_form': product_form})
my forms.py
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ('title', 'content', 'picture',)
urls.py
urlpatterns = patterns('',
url(r'^$', views.all, name='all'),
url(r'^add/$', views.add_product, name='add_product'),
url(r'^(?P<slug>[\w-]+)/$', views.single_product, name='single_product'),
)
template: products/add_product.html
<h1>Add Product</h1>
<form method="post" action="" enctype="multipart/form-data"> {% csrf_token %}
{{ product_form.as_p }}
<input type="submit" value="Add">
</form>
models.py
class Product(models.Model):
title = models.CharField(max_length=100)
author = models.ForeignKey(User)
content = models.CharField(max_length=300)
slug = models.SlugField(unique=True)
picture = models.ImageField(upload_to='products/picture/', blank=True)
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('single_product', kwargs={'slug': self.slug})
I fixed my view.py by adding following line :
form.author = request.user
and I removed the first line : author = request.user plus the instance as well.
So now works fine :)
Your code doesn't seem to be valid. And there should be errors.
The first thing that is notice is following:
author = request.user
product_form = ProductForm(request.POST, request.FILES, instance=author)
You are passing user instance as a instance to author form. That doesnt make any sense. For product form instance should a product, not a user. If you want to set a author field you can do following :
form = product_form.save(commit=False)
form.author = author
form.save()
Also its not good to call this variable form, because its not a form anymore, its a product object that is returned by save function.
I'm really stuck with this. To show my problem I created a new Django project and started from scratch, focusing only on one single form.
What I'm trying to do is to create a form with several fields of the same name. I tried using modelformset_factory to achieve this but it looks to me like it's not what I really need.
Below is my code (also on dpaste) which currently works fine with one single field called name. How can I create and process a form which would have several name fields? Could somebody point me in the right direction?
# models.py
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, blank=True, null=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
# forms.py
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = ('name',)
# views.py
def home(request):
if request.method == 'POST':
catform = CategoryForm(request.POST)
catformInstance = catform.save(commit = False)
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = CategoryForm()
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
# home.html template
<h3>Insert new Category</h3>
<form action="/" method="post" id="ingr-cat-form">{% csrf_token %}
{{ catform.as_p }}
<input type="submit" name="ingrCatForm" value="Save" />
</form>
UPDATE: to clarify, I want to allow user to insert several categories within one form. I think I'm getting close, here is my new version of views.py but it still stores just one category (the last one in the list):
def home(request):
if request.method == 'POST':
catform = CategoryForm(request.POST)
names = request.POST.getlist('name')
catformInstance = catform.save(commit = False)
for name in names:
catformInstance.name = name
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = CategoryForm()
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
You cannot have fields with the same name (on the same Model). If you only need to change the html label in the html form, use
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
name2 = models.CharField(max_length=30, unique=True, verbose_name="name")
user = models.ForeignKey(User, blank=True, null=True)
or
class CategoryForm(ModelForm):
def __init__(self , *args, **kwargs):
super(CategoryForm, self).__init__(*args, **kwargs)
self.fields['name2'].label = "name"
Here is a working solution. Thanks to #YardenST for pointing me in the right direction. I managed to solve my initial problem by following this tutorial.
# models.py
class Category(models.Model):
name = models.CharField(max_length=30, unique=True)
user = models.ForeignKey(User, blank=True, null=True)
class Meta:
verbose_name_plural = "Ingredience Categories"
def __unicode__(self):
return self.name
# forms.py
class CategoryForm(ModelForm):
class Meta:
model = Category
fields = ('name',)
# views.py
def home(request):
if request.method == 'POST':
catforms = [CategoryForm(request.POST, prefix=str(x), instance=Category()) for x in range(0,3)]
if all([cf.is_valid() for cf in catforms]):
for cf in catforms:
catformInstance = cf.save(commit = False)
catformInstance.save()
return HttpResponseRedirect('')
else:
catform = [CategoryForm(prefix=str(x), instance=Category()) for x in range(0,3)]
context = {'catform': catform}
return render_to_response('home.html', context, context_instance=RequestContext(request))
# home.html template
<h3>Insert new Category</h3>
<form action="/" method="post" id="ingr-cat-form">{% csrf_token %}
{% for catform_instance in catform %} {{ catform_instance.as_p }} {% endfor %}
<input type="submit" name="ingrCatForm" value="Save" />
</form>