我正在使用Django 1.8.4创建一个简单的应用程序,它允许用户共享来自其他网站的链接并投票和投票功能。有一些可重复使用的应用程序来处理这个,但我更喜欢编写自己的代码来“学习”Django。但经过几天的尝试,我觉得我的代码非常糟糕。它的工作方式几乎没问题,但它对我来说似乎并不是真正的pythonic,而且我认为这不是在Django中做事的正确方法。
以下是处理投票的代码部分:
models.py
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)
vote_date = models.DateTimeField(db_index=True, auto_now=True)
然后我将这个模型传递给一个简单的形式,这里是模板代码: link_detail.html
<form method="POST" action="{% url 'vote' %}" class="vote_form">
{% 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 }}"/>
<input type="hidden" id="id_link" name="vote_type" class="hidden_id" value="0"/>
<button> + </button>
[up votes: {{ link.up_votes}}]
</form>
<form method="POST" action="{% url 'vote' %}" class="vote_form">
{% 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 }}"/>
<input type="hidden" id="id_link" name="vote_type" class="hidden_id" value="1"/>
<button> - </button>
[down votes: {{ link.down_votes }}]
</form>
对我来说最丑陋的部分是views.py。我认为如果表达式具有更简单的函数,我最好更改许多重复:
class VoteFormView(FormView):
form_class = VoteForm
def form_valid(self, form):
v_user = self.request.user
v_link = get_object_or_404(Link, pk=form.data["link"])
v_type = form.data['vote_type']
up_votes = Vote.objects.filter(voter=v_user, link=v_link, vote_type=0)
down_votes = Vote.objects.filter(voter=v_user, link=v_link, vote_type=1)
up_voted = (up_votes.count() > 0)
down_voted = (down_votes.count() > 0)
if (v_type == "1") and not down_voted:
print ("%s has not voted down_voted %s") % (v_user, v_link)
Vote.objects.create(voter=v_user, link=v_link, vote_type=v_type)
with transaction.atomic():
Link.objects.filter(pk=form.data["link"]).update(down_votes=F('down_votes')+1)
print("down voted")
elif (v_type == "1") and down_voted:
down_votes[0].delete()
Link.objects.filter(pk=form.data["link"]).update(down_votes=F('down_votes')-1)
print("unvoted")
elif(v_type == "0") and not up_voted:
print ("%s has not voted up_voted %s") % (v_user, v_link)
Vote.objects.create(voter=v_user, link=v_link, vote_type=v_type)
Link.objects.filter(pk=form.data["link"]).update(up_votes=F('up_votes')+1)
print("voted")
elif (v_type == "0") and up_voted:
up_votes[0].delete()
Link.objects.filter(pk=form.data["link"]).update(up_votes=F('up_votes')-1)
print("unvoted")
return redirect("home")
我只是看了一些像django-vote这样的其他应用程序,这对我来说非常好看。但这不是我想要的,实际上我不明白它是如何完全运作的。
说出所有这些,这是我的问题:我想写一些单独的函数,检查用户之前是否已经投票,检查投票的类型,最后使用F()函数将投票应用于模型并增加用户的声誉。但我不知道在哪里放这些代码?例如在models.py中还是写新经理?
答案 0 :(得分:2)
只是一些快速观察:
在我看来,好像您在代码中放置了使用表单中的值的代码。那么为什么不把这个逻辑放在表单本身,可能是在save()方法中,因为VoteForm应该用于保存/创建/更新投票。
def save(self, *args, **kwargs):
kwargs['commit'] = False
vote = super(VoteForm, self).save(*args,**kwargs)
你不需要测试count()&gt; 0,因为空QS将返回False。我将逻辑拆分为两个嵌套的if语句,就像vtype isnt 0,它的1。
if v_type == '0':
if up_voted:
<do stuff>
else:
<do downvoted stuff>
else:
if up_voted:
<do stuff>
else:
<do downvoted stuff>
请注意,您有一个从super返回的未保存的Vote实例,因此您可以使用它来代替上面的create语句。我还将创建一个可以upvote / downvote的LinkManager类。您的F语句使用大概是为了速度,因为它不会将Link拉入内存。也不需要使用过滤器,因为pk是唯一的。
LinkManager(models.Manager):
def update_up_votes(self, pk, increment=True):
self.get(pk=pk).update(up_votes=F('up_votes')+1) if increment else self.get(pk=pk).update(up_votes=F('up_votes')-1)
希望其中一些对于让你进行一些重构很有用。
编辑:
您可以在代码中使用LinkManager方法代替Link.objects.filter()。update()。它将它重构给经理。所以对于upvoting
Link.objects.update_up_votes(pk)
或递减upvote
Link.objects.update_up_votes(pk, increment=False)
请记住,您必须通过对象将自定义管理器添加到Link类。
对于您的模板 - 我并没有真正看到它,但只是在VoteForm上执行{{form.as_p}}将为您提供已格式化的所有字段。你不需要两个表格,因为他们发布到同一个视图。只是在视图中做你的逻辑来决定。您也不需要访问该用户,因为您的投票通过vote.voter获得。