在 Django 中编写函数的 pythonic 和 MVC 友好方法是什么?
What are the pythonic & MVC friendly ways to write functions in Django?
我正在使用 Django 1.8.4 创建一个简单的应用程序,它允许用户共享来自其他网站的链接以及投票赞成和反对的功能。有一些可重用的应用程序可以处理这个问题,但我更喜欢将自己的代码编写到 "learn" 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。我想我最好用更简单的函数改变许多重复的 if 表达式:
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 或写新经理?
只是一些快速观察:
在我看来,您似乎正在放置使用视图中表单中的值的代码。那么为什么不将该逻辑放在表单本身中,可能放在 save() 方法中,因为 VoteForm 应该用于 saving/creating/updating 投票。
def save(self, *args, **kwargs):
kwargs['commit'] = False
vote = super(VoteForm, self).save(*args,**kwargs)
您不需要测试 count() > 0,因为空 QS 将 return 为假。我将逻辑分成两个嵌套的 if 语句,就好像 vtype 不是 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>
请注意,您有一个未保存的 Vote returned 来自 super 的实例,因此您可以使用它来代替上面的创建语句。我还会制作一个可以 upvote/downvote 的 LinkManager class。您的 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()。它将它重构给经理。所以为了投票
Link.objects.update_up_votes(pk)
或减少赞成票
Link.objects.update_up_votes(pk, increment=False)
请记住,您必须通过对象将自定义管理器添加到 Link class。
对于您的模板 - 我并没有真正查看它,但只需在 VoteForm 上执行 {{form.as_p}} 即可为您提供所有已格式化的字段。您不需要两种形式,因为它们 post 用于相同的视图。只需在视图中执行您的逻辑即可决定。您也不需要在那里访问用户,因为您的投票是通过 vote.voter.
我正在使用 Django 1.8.4 创建一个简单的应用程序,它允许用户共享来自其他网站的链接以及投票赞成和反对的功能。有一些可重用的应用程序可以处理这个问题,但我更喜欢将自己的代码编写到 "learn" 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。我想我最好用更简单的函数改变许多重复的 if 表达式:
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 或写新经理?
只是一些快速观察:
在我看来,您似乎正在放置使用视图中表单中的值的代码。那么为什么不将该逻辑放在表单本身中,可能放在 save() 方法中,因为 VoteForm 应该用于 saving/creating/updating 投票。
def save(self, *args, **kwargs):
kwargs['commit'] = False
vote = super(VoteForm, self).save(*args,**kwargs)
您不需要测试 count() > 0,因为空 QS 将 return 为假。我将逻辑分成两个嵌套的 if 语句,就好像 vtype 不是 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>
请注意,您有一个未保存的 Vote returned 来自 super 的实例,因此您可以使用它来代替上面的创建语句。我还会制作一个可以 upvote/downvote 的 LinkManager class。您的 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()。它将它重构给经理。所以为了投票
Link.objects.update_up_votes(pk)
或减少赞成票
Link.objects.update_up_votes(pk, increment=False)
请记住,您必须通过对象将自定义管理器添加到 Link class。
对于您的模板 - 我并没有真正查看它,但只需在 VoteForm 上执行 {{form.as_p}} 即可为您提供所有已格式化的字段。您不需要两种形式,因为它们 post 用于相同的视图。只需在视图中执行您的逻辑即可决定。您也不需要在那里访问用户,因为您的投票是通过 vote.voter.