什么是pythonic& MVC友好的方式在Django中编写函数?

时间:2015-10-11 12:37:22

标签: python django django-models django-forms

我正在使用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中还是写新经理?

1 个答案:

答案 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获得。