I'm creating a Django 1.8.4 web application to submit links with up vote and down vote functionality. But every time I click the up vote button, it applies on the last link submitted. I'm using forms to do it. The form is quite normal:
class VoteForm(forms.ModelForm):
class Meta:
model = Vote
exclude = ("vote_type", "vote_date",)
here's the model:
class Link(models.Model):
title = models.CharField(max_length=200)
...
up_votes = models.IntegerField(default=0, blank=True, db_index=True)
down_votes = models.IntegerField(default=0, blank=True, db_index=True)
...
class Vote(models.Model):
UP, DOWN = range(2)
TYPE_CHOICES = [(UP, "Upvote"), (DOWN, "DownVote")]
voter = models.ForeignKey(User)
link = models.ForeignKey(Link, related_name='votes')
vote_type = models.IntegerField(choices=TYPE_CHOICES, db_index=True, null=True)
vote_date = models.DateTimeField(db_index=True, auto_now=True)
and the view handles voting: (it's still so simple and stupid I guess)
class VoteFormView(FormView):
form_class = VoteForm
def form_valid(self, form):
link = get_object_or_404(Link, pk=form.data["link"])
user = self.request.user
prev_votes = Vote.objects.filter(voter=user, link=link)
has_voted = (prev_votes.count()>0)
if not has_voted:
Vote.objects.get_or_create(voter=user, link=link)
Link.objects.filter(pk=form.data["link"]).update(up_votes=F('up_votes')+1)
print("+voted")
else:
pass
return redirect("home")
The idea here was to check if the current user has voted for the link, if she didn't so, then increment the up_votes field of corresponding link object. But now it only increments the last link.object.up_votes.
As you may see in comments, the problem caused by wrong pk sent to the views, and it was my mistake to close tag in my template. Just for the record I paste here that part of template code:
{% for link in object_list %}
<form method="POST" action="{% url 'vote' %}" class="vote_form">
<li>
{% csrf_token %}
<input type="hidden" id="id_link" name="link" class="hidden_id" value="{{ link.pk }}"/>
<input type="hidden" id="id_link" name="voter" class="hidden_id" value="{{ user.pk }}"/>
<button>+</button>
[{{ link.up_votes }}]
<a href="{{ link.url }}">
<h3>{{ link.title }}</h3>
</a>
<a href="{% url 'link_detail' pk=link.id slug=link.title|slugify %}">
Comments
</a>
<p>
Submitted by: {{ link.submitter }}
</p>
</li>
</form>
{% endfor %}
Related
I am trying to combine Django's generic UpdateView and hidden input field. My purpose is to track whether a post was edited after it was created or not. Hidden field name is "updated".
views.py:
class PostUpdateView(UpdateView):
model = Post
template_name = 'journal/post_update_form.html'
form_class = PostUpdateForm
success_url = reverse_lazy('my_posts')
models.py:
class Post(models.Model):
title = models.CharField(max_length=500)
text = models.TextField(max_length=2000, null=True, blank=True)
owner = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True)
#storing the link to media files in CharField
image = models.CharField(max_length=500, null=True, blank=True)
audio = models.CharField(max_length=500, null=True, blank=True)
video = models.CharField(max_length=500, null=True, blank=True)
rubric = models.CharField(max_length=100, default="No rubric", choices=RUBRIC_CHOICES)
private = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
updated = models.CharField(max_length=10, default="False")
forms.py:
from django import forms
from .models import Post
class PostUpdateForm(forms.ModelForm):
class Meta:
model = Post
fields = ["title", "text", "image", "audio", "video", "rubric", "private", "updated"]
widgets = {'updated': forms.HiddenInput(attrs={'value':"updated"})}
relevant part of update form template:
<form action="" method="POST">
{% csrf_token %}
{% for field in form %}
{% if field != form.rubric and field != form.private %}
<div class="mb-3 form-group">
<label for="{{field.name}}" class="form-label">{{field.label}}</label>
{{field.errors}}
{% if field == form.text %}
<textarea type="text" class="form-control" id="{{field.name}}" name="{{field.name}}">{{field.value|default_if_none:"" }}</textarea>
{% elif field == form.updated %}
<input type="hidden" id="{{field.name}}" name="{{field.name}}" value="updated">
{% else %}
<input type="text" class="form-control" id="{{field.name}}" name="{{field.name}}" value="{{field.value|default_if_none:"" }}">
{% endif %}
</div>
<...some other fields...>
{% endif %}
{% endfor %}
<input type="submit" class="btn btn-primary" value="Update"/>
</form>
Value of "updated" field is passed successfully, but the field value is visible (non-editable).
In forms.py I tried to:
exclude 'updated' from fields list. Result: the value of "updated" is not saved.
use "widgets = {'updated': forms.HiddenInput}". Result: value is also passed successfully, but the field is still visible.
use only widgets in forms.py and comment out input field [1] in the template. Result: field is visible, editable, value is not saved.
[1] input field:
{% elif field == form.updated %}
<input type="hidden" id="{{field.name}}" name="{{field.name}}" value="updated">
I would appreciate any insights about what might be wrong here. Thank you in advance.
In PostUpdateForm you can do so:
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['updated'].initial = 'updated'
self.fields['updated'].disabled = True
https://docs.djangoproject.com/en/4.1/ref/forms/fields/#disabled
I have encountered a funny challenge, as am building some apps where they want track some expenses and revenues profits ...
I used generic views to create and get details and delete, all work fine with expenses, but I copied the same code and I used it for revenues from expenses you can't imagine this view is rendering a button only yet it has a form, NOTE SAME CODE WORK WELL ON EXPENSES. I even exchanged templates but it kept rendering the former content even after changing the template. it doesn't matter what's in the template the view just renders the same thing even when I remove the form, even when I put pass.
class AccountsRevenueCreate(CreateView):
template_name='dashboard/expense_create.html'
model= AccountsExpense
success_url = reverse_lazy('dashboard:expenses')
form_class=AccountsExpenseForm
and the form
class AccountsRevenueForm(forms.ModelForm):
class Meta:
model = AccountsRevenue
fields = '__all__'
and in the template
<div class="row">
<div class="col-12">
<h5 class="form-title"><span>revenue Update
</span></h5>
</div>
<form method="post", action="{% url
'dashboard:revenue_add' %}">
{% csrf_token %}
{% comment %} {{ form|crispy }} {% endcomment %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary col-12 col-sm-12">Save
</button>
</form>
</div>
and the URLs
path ('accounts/revenues/create/', AccountsRevenueCreate.as_view(), name='revenue_create'),
I have chosen to show you only where the problem is not paste unnecessary code
here are the models you requested me to update
from django.db import models
class AccountsRevenue(models.Model):
revenue_of = models.CharField(max_length=250, default='',null=False)
revenue_by = models.CharField(max_length=250, default='',null=False)
amount = models.IntegerField( default='100,000')
day_on_which = models.DateField(null=True, blank=True)
evidence_document = models.ImageField(upload_to = 'uploads/', blank=True, default='')
signature = models.ImageField(upload_to = 'uploads/', blank=True, default='')
def __str__(self):
return str(self.revenue_of)
def get_absolute_url(self):
return reverse('dashboard:revenue', kwargs= {'pk':self.pk} )
def get_document(self):
if self.evidence_document and hasattr(self.evidence_document, 'url'):
return self.evidence_document.url
else:
return "/static/assets/img/user.png"
class AccountsExpense(models.Model):
expense_of= models.CharField(max_length=250, default='',null=False)
expense_by = models.CharField(max_length=250, default='',null=False)
amount = models.IntegerField( default='100,000')
day_on_which = models.DateField(null=True, blank=True)
evidence_document = models.ImageField(upload_to = 'uploads/',
blank=True, default='')
signature = models.ImageField(upload_to = 'uploads/', blank=True,
default='')
def __str__(self):
return str(self.expense_of)
def get_absolute_url(self):
return reverse('dashboard:expense', kwargs= {'pk':self.pk} )
def get_document(self):
if self.evidence_document and hasattr(self.evidence_document,
'url'):
return self.evidence_document.url
else:
return "/static/assets/img/user.png"
from django.urls import URLPattern, path
from django.conf.urls.static import static
from. views import profiles, Profile_detail, Profile_update,
Profile_delete, dashboard, Profile_create,\
AccountsRevenues, revenue_detail_view, AccountsRevenueUpdate,
AccountsRevenue_delete, \
AccountsExpenses, AccountsExpenseDetail, AccountsExpenseCreate,
AccountsExpenseUpdate, AccountsExpenseDelete,\
userprofileupdate, revenue_add, AccountsRevenueCreate
app_name = "dashboard"
urlpatterns = [
path ('accounts/revenues', AccountsRevenues.as_view(), name='revenues'),
path ('accounts/revenues/<str:pk>/', revenue_detail_view,
name='revenue'),
path ('accounts/revenues/<str:pk>/update/',
AccountsRevenueUpdate.as_view(), name='revenue_update'),
path ('accounts/revenues/<str:pk>/delete/',
AccountsRevenue_delete.as_view(), name='revenue_delete'),
path ('accounts/revenues/create/', AccountsRevenueCreate.as_view(),
name='revenue_create'),
# accounts expenses
path ('accounts/expenses', AccountsExpenses.as_view(), name='expenses'),
path ('accounts/expenses/<str:pk>', AccountsExpenseDetail.as_view(),
name='expense'),
path ('accounts/expenses/<str:pk>/update/',
AccountsExpenseUpdate.as_view(), name='expense_update'),
path ('accounts/expenses/<str:pk>/delete/',
AccountsExpenseDelete.as_view(), name='expense_delete'),
path ('accounts/expenses/create/', AccountsExpenseCreate.as_view(),
name='expense_create'),
]
I have added a screenshot of what it comes out as enter image description here
left model.py, forms.py, urls.py as it was.
views.py
in the line: template_name = 'bboard/expense_create.html' replace bboard with the name of the folder where the templates are located. I have it templates/bboard. Also in your field: form_class=AccountsExpenseForm changed to form_class = AccountsRevenueForm.
And the return goes to the same page with the form: success_url = reverse_lazy('revenue_create').
success_url can then be returned to what it was, now you need to make sure that everything works.
views.py
class AccountsRevenueCreate(CreateView):
template_name = 'bboard/expense_create.html'
model = AccountsExpense
success_url = reverse_lazy('revenue_create')
form_class = AccountsRevenueForm
expense_create.html
in the template you have an error form method="post", there should not be a comma.
<div class="row">
<div class="col-12">
<h5 class="form-title"><span>revenue Update
</span></h5>
</div>
<form method="post" action="{% url 'revenue_create' %}">
{% csrf_token %}
{% comment %} {{ form|crispy }} {% endcomment %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary col-12 col-sm-12">Save
</button>
</form>
</div>
Followed the link: http://localhost:8000/accounts/revenues/create/
Screenshot attached:
I'm trying to post comment but it's not getting posted, rather this error is appearing UNIQUE constraint failed: players_comment.user_id. I don't know why this error is occuring.
My forms.py:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('body', 'transfernews')
My models.py :
class Transfernews(models.Model):
player_name = models.CharField(max_length=255)
player_image = models.CharField(max_length=2083)
player_description = models.CharField(max_length=3000)
date_posted = models.DateTimeField(default=timezone.now)
class Comment(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
transfernews = models.ForeignKey(Transfernews, related_name="comments", on_delete=models.CASCADE)
body = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
return '%s - %s' % (self.transfernews.player_name, self.user.username)
My views.py:
def transfer_targets(request):
transfernews = Transfernews.objects.all()
form = CommentForm(request.POST or None)
if form.is_valid():
new_comment = form.save(commit=False)
new_comment.user = request.user
new_comment.save()
return redirect('transfernews/')
return render(request, 'transfernews.html', {'transfernews': transfernews, 'form': form})
My transfernews.html:
{% for transfer in transfernews %}
{% if not transfer.comments.all %}
No comments Yet...
{% else %}
{% for comment in transfer.comments.all %}
<strong>
{{ comment.user.username }} - {{ comment.date_added }}
</strong>
<br/>
{{ comment.body }}
<br/><br/>
{% endfor %}
{% endif %}
<hr>
<div>Comment and let us know your thoughts</div>
<form method="POST">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary btn-sm shadow-none" type="submit">Post comment</button>
<button class="btn btn-outline-primary btn-sm ml-1 shadow-none" type="button">Cancel</button>
</form>
{% endfor %}
In models.py, in the comment class, change
user = models.OneToOneField(User, on_delete=models.CASCADE)
to
user = models.ForeignKey(to=User, on_delete=models.CASCADE)
One to one works both ways, user's allowed to only have one comment and a comment can belong to only one user. By changing to one to many via foreignkey you'll preserve the latter and get rid of the former constraint.
So I have two models. Deck and Card. When the user creates card it should be tied to a deck, in a ManyToMany relationship.
The card is created through the Generic Django create view, and I can't crack how I can assign the card to a deck, in this context. Any ideas on how I might solve this?
My CreateView
class CardCreateView(LoginRequiredMixin, CreateView):
model = Card
fields = ['question', 'answer']
def form_valid(self, form):
form.instance.creator = self.request.user
return super().form_valid(form)
def get_success_url(self):
return reverse('spaced_repitition-home')
def assign_card(self, deck_id): #It's here I don't get why it doesn't work
card = self.get_object()
deck = get_object_or_404(Deck, pk=deck_id)
card.decks.add(deck)
card.save()
Template that sends user to form (passes on deck_id)
{% for deck in decks reversed %}
<a href="{% url 'card-create' deck_id=deck.id %}">
<p> Add Card </>
{% endfor %}
Form Template
{% extends "spaced_repitition/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class=form-group>
<legend class="borders-bottom mb-4"> Create Card </legend>
{{ form|crispy }}
<div class=form-group>
<button class= "btn btn-outline-info" type="submit"> Create </button>
</div>
</fieldset>
</form>
{% endblock content %}
Models
class Deck(models.Model):
title = models.CharField(max_length=100)
date = models.DateTimeField(default=timezone.now)
creator = models.ForeignKey(User, on_delete=models.CASCADE)
description = models.TextField(max_length=200, blank=True)
def __str__(self):
return self.title
class Card(models.Model):
question = models.CharField(max_length=100)
answer = models.TextField()
date = models.DateTimeField(default=timezone.now)
creator = models.ForeignKey(User, on_delete=models.CASCADE)
decks = models.ManyToManyField(Deck)
days_till_study = models.IntegerField(default=1)
def __str__(self):
return self.question + ' pk: ' + str(self.pk)
Url
path('card/new/<int:deck_id>/', CardCreateView.as_view(), name='card-create'),
Thanks for reading this.
Edit
Found the solution here:
Django Createview default value for a foreign key field
Hello I have been recently working on Django search forms recently and have tired to edit one myself. Here is a search form that is supposed to find Clients. However When I type in a clients name, it does not display that client's name. So I am wondering What I am doing wrong.
#model.py
class Client(models.Model):
company = models.CharField(max_length=80)
first_name = models.CharField(max_length=80, blank=True, null=True)
last_name = models.CharField(max_length=80, blank=True, null=True)
address = models.CharField(max_length=250)
city = models.CharField(max_length=100)
country = models.CharField(max_length=120)
postcode = models.CharField(max_length=7)
telephone = models.CharField(max_length=20)
email = models.EmailField()
additional_info = models.TextField(blank=True, null=True)
def __unicode__(self):
return self.company
#views.py
#login_required
def search_client(request):
query = request.GET.get('q', '')
if query:
qset = (
Q(company__icontains=query) |
Q(address__icontains=query) |
Q(postcode__icontains=query)
)
results = Client.objects.filter(qset).distinct()
else:
results = []
return render_to_response("search_clients.html", {
"results": results,
"query": query
}, context_instance=RequestContext(request))
#search_clients
{% extends "base.html" %}
{% block content %}
<h1>Search</h1>
<form action="." method="GET">
<label for="q">Search: </label>
<input type="text" name="q" value="{{ query|escape }}">
<input type="submit" value="Search">
</form>
{% if query %}
<h2>Results for "{{ query|escape }}":</h2>
{% if results %}
<ul>
{% for clients in results %}
<li>{{ clients|escape }}</l1>
{% endfor %}
</ul>
{% else %}
<p>No clients found</p>
{% endif %}
{% endif %}
{% endblock %}
Could it be because you are searching by company, address and postcode, and not by client name?
Ok looks like somehow it know works properly. The odd thing is I don't know how it started to working properly. I may have restarted the server again while making changes or may have been my urls.py file. I really don't know but seems ok now.