Python Django:在视图中,最好是为对象添加属性还是创建数据字典?

前端之家收集整理的这篇文章主要介绍了Python Django:在视图中,最好是为对象添加属性还是创建数据字典?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
在这种情况下,我的模型并不重要,我认为这是一个基本的 Python问题.

假设我有一个项目的查询集,我想计算每个要在模板中显示的东西.

在我看来,我可以创建一个对象列表,对于每个对象,我可以在该对象上设置一个属性进行计算,然后我可以在模板中显示它.或者我可以创建一个字典列表,只获取我需要在每个字典中显示的字段以及计算字段.对于性能和一般实践来说哪个更好?

一个过于简化的示例,为了清晰起见(我知道我可以从模板中调用getAge(),我真正计算的是更复杂的,对于性能,我想在视图代码中进行计算):

models.py:

class Person(models.Model):
    first_name = ...
    last_name = ...
    date_of_birth = ...
    .
    .
    .
    def getAge(self):
        return ... # return the calculated years since date_of_birth

views.py:

def method1_object_property(request):
    people = Person.objects.all()
    for p in people:
        p.age = p.getAge()
    return render_to_response('template.htm',{'people': people})

def method2_dictionary(request):
    people = Person.objects.all()
    data = list()
    for p in people:
        row = dict()
        row['first_name'] = p.first_name
        row['last_name'] = p.last_name
        row['age'] = p.getAge()
        data.append(row)
    return render_to_response('template.htm',{'people': data})

template.htm:

<ul>
    {% for p in people %}
        {{ p.first_name }} {{ p.last_name }} (Age: {{ p.age }})
    {% endfor %}
</ul>

到目前为止,这两种方法都可以正常工作,我只是好奇首选方法是什么以及为什么.是否存在使用对象点属性方法(object.new_field =’some_detail’)将新字段动态分配给内存中现有对象的性能问题?

更新:

是的,我知道在我的例子中我可以从模板调用getAge(),是的,这是方法的不正确的命名标准,应该是带有下划线的小写.我认为我的例子太简单了,而且让我真正想知道的事情蒙上阴影.

将信息添加到我想要在视图中显示的对象的最佳方法是什么,该视图不是模型层的一部分.假设我得到一个Person对象的QuerySet,并想要计算他们在过去30,60和90天登录我网站的次数.我想动态为每个Person对象创建三个“属性”.我可以在视图中设置它

for p in people:
    p.last_30 = Login.objects.filter(person=p,login_date__gt=date.today()-timedelta(days=30))
    p.last_60 = Login.objects.filter(person=p,login_date__gt=date.today()-timedelta(days=60))
    p.last_90 = Login.objects.filter(person=p,login_date__gt=date.today()-timedelta(days=90))

然后在我的模板中,我可以显示那些“属性”.我只是想确保我没有违反某些Python标准或欺骗系统.或者,我可以将这些其他查找存储在字典中,其中对象在一个键/对中,并且各个细节在单独的键中.在视图中这是一个更多的工作,但我很好奇是否更好的性能或标准的合规性这样做?

对不起,如果我的原始问题不够清楚,或者我的例子增加了混乱.

解决方法

绝对方法1.

方法2毫无意义,您可以直接在模板中迭代查询集,无需在视图中构建中间的“词典列表”.例如,你可以这样做:

def method2_dictionary(request):
    people = Person.objects.all()
    return render_to_response('template.htm',{'people': people})

在您的模板中:

{% for p in people %}
    {{ p.first_name }}
    etc
{% endfor %}

回到方法1 ……

这个:p.age = p.getAge()也没有意义,你可以直接调用模板中的方法{{p.getAge}}(只要你的方法不带参数),请看这里的文档:
https://docs.djangoproject.com/en/dev/topics/templates/#accessing-method-calls

请注意,在Python中,我们通常更喜欢使用“带下划线的小写”作为方法名称,例如def get_age(self)和{{p.get_age}}
(参见http://www.python.org/dev/peps/pep-0008/#function-names官方’PEP8’样式指南)

如果你的get_age方法没有side-effects并且没有参数你可能想让它成为一个属性,这是Python的方法,你可以访问没有括号的getter方法.

在这种情况下,将它命名为年龄是有意义的:

@property
def age(self):
    return ... # return the calculated years since date_of_birth

并在您的模板中:

{% for p in people %}
    {{ p.first_name }}
    {{ p.age }}
    etc
{% endfor %}

有关Python属性的更多信息,请参见此处:
http://docs.python.org/2/library/functions.html#property

在这个SO问题中的更多信息:
Real world example about how to use property feature in python?

UPDATE

参考您更新的问题…作为样式问题,我仍然会在模型上创建这些(last_30等)方法,而不是在视图代码中的每个模型实例上添加ad hoc属性.

性能的角度来看,在大多数现实世界中,方法查找与字典等的内存,处理时间等的差异是微不足道的……到目前为止,这种代码中最大的性能考虑通常是数据库查询数量.

如果您知道要为查询集中的每个项目执行额外的查询(或三个),那么值得寻找在一个或多个大查询获取所有内容方法.

在某些情况下,您可以使用annotate()方法
https://docs.djangoproject.com/en/dev/ref/models/querysets/#annotate

(我认为这不可能在你的例子中)

在上面的特定代码中,您只需查询90天(最早的间隔),您可以在Python中过滤60天和30天的集,而无需再次查询数据库.

但是,对于人员查询集中的每个项目,仍然会进行一次额外查询.最好为人员的所有(或任何子集)的Login对象执行一个大查询.由于登录时Person有一个外键关系,我们可以在查询Login模型时使用select_related()在一个大查询获取Person实例:

def method3(request):
    logins = Login.objects.filter(
        person__in=Person.objects.all(),login_date__gt=date.today() - timedelta(days=90)
    ).order_by('person','login_date').select_related()
    return render_to_response('template.htm',{'logins': logins})

注意,如果你真的在做Person.objects.all(),你就不需要上面的person__in过滤器,只要你想以某种方式过滤Person集.

现在我们在一个大查询中获得了所有数据,我们可以在python端执行我们需要的操作来显示数据.例如,在模板中我们可以使用regroup标签

{% regroup logins by person as people %}
{% for person in people %}
    {% with person.grouper as p %}
        {{ p.first_name }}
        {% for login in person.list %}
            {{ login.login_date }}
        {% endfor %}
    {% endwith %}
{% endfor %}

你可以更进一步,为登录日期范围写一个自定义标签…我不会在这里详细说明,但在你的模板中它可能看起来像:

{% regroup logins by person as people %}
{% for person in people %}
    {% with person.grouper as p %}
        {{ p.first_name }}
        {% logins_since person.list 60 as last_60_days %}
        {% logins_since person.list 30 as last_30_days %}
        {% for login in last_30_days %}
            {{ login.login_date }}
        {% endfor %}
        {% for login in last_60_days %}
            {{ login.login_date }}
        {% endfor %}
    {% endwith %}
{% endfor %}

猜你在找的Python相关文章