【问题标题】:How should a model object be passed to a helper function in Django如何将模型对象传递给 Django 中的辅助函数
【发布时间】:2017-10-05 12:11:53
【问题描述】:

在 Django 中将模型对象传递给辅助函数的正确方法是什么?

例如,我有一个用户对象:

u = User.objects.get(pk=1)  

然后想在保存回数据库之前使用函数helpers.foo()u 执行一些操作。

我应该传递整个对象并直接操作吗?或者只是 ID 然后在帮助函数中重新查询数据库以再次获取对象...

【问题讨论】:

  • 数据库查询既不免费也不便宜;虽然很快。
  • 如果同步完成,则传递实例。对于像 celery 任务这样的异步调用,传递 id(无论如何只有 id 可以序列化)。

标签: python django django-models


【解决方案1】:

让我们做一个小测试:

>>> from django.contrib.auth.models import User
>>> u1 = User.objects.get(pk=1)
>>> u2 = User.objects.get(pk=1)
>>> u1 == u2
True
>>> u1 is u2
False
>>> u1.first_name
u'Ruth'
>>> u2.first_name
u'Ruth'
>>> u2.first_name = u"Route"
>>> u2 == u1
True
>>> u1.first_name
u'Ruth'
>>> u2.save()
>>> u1.first_name
u'Ruth'
>>> 
>>> u1.refresh_from_db()
>>> u1.first_name
u'Route'

如您所见,两次加载相同的记录会产生两个不同的对象(它们比较相等,因为 Model 在 pk 上进行比较),并且更改一个 - 无论您是否保存更改 - 不会影响第二个(直到你当然从数据库中刷新它)。此外,如果您同时修改并保存两者,结果可能与您的预期不同:

>>> u1.last_name
u'FOOBAR'
>>> u2.last_name
u'FOOBAR'
>>>
>>> u1.first_name = u"Ruth"
>>> u2.last_name = u"ROOTZ"
>>> u1.save()
>>> u2.save()
>>> ux = User.objects.get(pk=1)
>>> ux.first_name
u'Route'
>>> ux.last_name
u'ROOTZ'

如果您考虑数据库访问成本,显然您不希望您的辅助函数从数据库加载第二个(不同的)实例。

异步调用的情况(通常是 celery 任务)是不同的——在这里你想传递对象的 id 并让你的任务从数据库中重新加载它。主要原因是数据库记录可能在任务调用和执行之间已被修改(并且您不想覆盖这些修改)。

【讨论】:

  • 太棒了,很好的答案。感谢您的澄清
  • @Rob 不客气 - 并随时支持和/或接受答案。
【解决方案2】:

传递完整对象的更好方法,如果你只传递 id 那么你必须再次点击 db 来获取用户。

【讨论】:

    【解决方案3】:

    如果您是一般性地询问,我已将一个工作项目中的一部分添加到底部。但是,在您的情况下,我相信您可以定义一个实例方法来处理选定的对象——当然,如果这些操作与模型直接相关。

    class User(models.Model):
        first_name = models.CharField(max_length=40)
        last_name = models.CharField(max_length=40)
        title = models.CharField(max_length = 50)
        ...
    
        def make_comrade(self):
            return "Comrade " + self.last_name
    

    那么,

    user = User.objects.get(pk=1)
    user.update(title=user.make_comrade())
    

    我上面说过的代码。

    在我的 views.py

    from reports_helper import report_generator
    
    ...
    
    def export_terminals_daily_turnover(request):
        return report_generator('TerminalTurnover', 'daily_turnover', 'terminal_id', 'address_on_bill', 'summ')
    

    这是我的 reports_helper.py

    import csv
    import django
    from django.http import  HttpResponse
    
    
    def get_model_name(model_name):
        entity = django.apps.apps.get_model('reports', model_name)
        return entity
    
    
    def report_generator(model_name, report_name, *args):
        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename="{}.csv"'.format(report_name)
    
        headers = list(args)
        klass = get_model_name(model_name)
    
        writer = csv.writer(response)
        writer.writerow(headers)
    
        rows = klass.objects.all().values_list(*args)
        for row in rows:
            writer.writerow(row)
    
        return response
    

    顺便说一句,我这里指的模型是一个数据库视图而不是一个表

    class TerminalTurnover(models.Model):
        terminal_id = models.IntegerField(primary_key=True)
        summ = models.DecimalField(max_digits=15, decimal_places=2)
        address_on_bill = models.CharField(max_length=100)
    
        class Meta:
            managed = False
            db_table = 'reports_terminalturnover'
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-16
      • 1970-01-01
      • 2017-03-23
      • 1970-01-01
      • 2015-11-18
      • 2016-04-16
      • 1970-01-01
      相关资源
      最近更新 更多