Model Mixin用于对象历史

时间:2017-03-07 12:20:46

标签: python django django-models

我正在尝试创建一个跟踪对象/实例历史记录的Mixin。这个想法看起来很简单:覆盖models.Model的save方法,在更改之前抓取对象,保存更改,然后比较以查看哪些字段已更改并将更改保存到另一个表:

class ObjectHistoryMixin(object):

def save(self, *args, **kwargs):
    previous_state = self.__class__.objects.get(pk=self.pk)
    super(ObjectHistoryMixin, self).save(*args, **kwargs)
    new_state = self
    fields = self.__class__._meta.get_fields(include_parents=False)
    for field in fields:
        if getattr(previous_state, field.name) != getattr(new_state, field.name):
            print('field {} changed'.format(field))

我在这个模型上尝试过:

class Insurer(ObjectHistoryMixin, models.Model):

    created_date = models.DateTimeField(auto_now_add=True)
    created_by = models.ForeignKey('users.User', on_delete=models.PROTECT)

    name = models.CharField('naam', max_length=100)

但这不起作用:

AttributeError at /portal/beheer/verzekeraars/9/bewerken/

'Insurer' object has no attribute 'submittedprescription'

Request Method:     POST
Request URL:    http://127.0.0.1:8000/portal/beheer/verzekeraars/9/bewerken/
Django Version:     1.11b1
Exception Type:     AttributeError
Exception Value:    

'Insurer' object has no attribute 'submittedprescription'

Exception Location:     C:/Users/Administrator/SVN/doras_val\portal\models.py in save, line 20
Python Executable:  C:\Users\Administrator\SVN\venvs\venv_doras_val\Scripts\python.exe
Python Version:     3.6.0
Python Path:    

['C:/Users/Administrator/SVN/doras_val',
 'C:\\Program Files (x86)\\JetBrains\\PyCharm 2016.3.2\\helpers\\pydev',
 'C:\\Users\\Administrator\\SVN\\doras_val',
 'C:\\Program Files (x86)\\JetBrains\\PyCharm 2016.3.2\\helpers\\pydev',
 'C:\\Users\\Administrator\\SVN\\venvs\\venv_doras_val\\Scripts\\python36.zip',
 'C:\\Users\\Administrator\\SVN\\venvs\\venv_doras_val\\DLLs',
 'C:\\Users\\Administrator\\SVN\\venvs\\venv_doras_val\\lib',
 'C:\\Users\\Administrator\\SVN\\venvs\\venv_doras_val\\Scripts',
 'c:\\python36-32\\Lib',
 'c:\\python36-32\\DLLs',
 'C:\\Users\\Administrator\\SVN\\venvs\\venv_doras_val',
 'C:\\Users\\Administrator\\SVN\\venvs\\venv_doras_val\\lib\\site-packages']

Server time:    di, 7 Mrt 2017 13:15:38 +0100
Environment:


Request Method: POST
Request URL: http://127.0.0.1:8000/portal/beheer/verzekeraars/9/bewerken/

Django Version: 1.11b1
Python Version: 3.6.0
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'django.contrib.humanize',
 'crispy_forms',
 'public',
 'users',
 'portal',
 'portal_patient',
 'portal_pharmacy',
 'portal_manager']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'portal_patient.middleware.ActivePatientMiddleware',
 'portal_pharmacy.middleware.ActivePharmacyMiddleware']



Traceback:

File "C:\Users\Administrator\SVN\venvs\venv_doras_val\lib\site-packages\django\core\handlers\exception.py" in inner
  41.             response = get_response(request)

File "C:\Users\Administrator\SVN\venvs\venv_doras_val\lib\site-packages\django\core\handlers\base.py" in _get_response
  187.                 response = self.process_exception_by_middleware(e, request)

File "C:\Users\Administrator\SVN\venvs\venv_doras_val\lib\site-packages\django\core\handlers\base.py" in _get_response
  185.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\Users\Administrator\SVN\venvs\venv_doras_val\lib\site-packages\django\views\generic\base.py" in view
  68.             return self.dispatch(request, *args, **kwargs)

File "C:/Users/Administrator/SVN/doras_val\portal_manager\mixins.py" in dispatch
  11.         return super(PermissionRequiredMixin, self).dispatch(request, *args, **kwargs)

File "C:\Users\Administrator\SVN\venvs\venv_doras_val\lib\site-packages\django\views\generic\base.py" in dispatch
  88.         return handler(request, *args, **kwargs)

File "C:\Users\Administrator\SVN\venvs\venv_doras_val\lib\site-packages\django\views\generic\edit.py" in post
  240.         return super(BaseUpdateView, self).post(request, *args, **kwargs)

File "C:\Users\Administrator\SVN\venvs\venv_doras_val\lib\site-packages\django\views\generic\edit.py" in post
  183.             return self.form_valid(form)

File "C:\Users\Administrator\SVN\venvs\venv_doras_val\lib\site-packages\django\contrib\messages\views.py" in form_valid
  11.         response = super(SuccessMessageMixin, self).form_valid(form)

File "C:\Users\Administrator\SVN\venvs\venv_doras_val\lib\site-packages\django\views\generic\edit.py" in form_valid
  162.         self.object = form.save()

File "C:\Users\Administrator\SVN\venvs\venv_doras_val\lib\site-packages\django\forms\models.py" in save
  451.             self.instance.save()

File "C:/Users/Administrator/SVN/doras_val\portal\models.py" in save
  20.             print(getattr(previous_state, field.name))

Exception Type: AttributeError at /portal/beheer/verzekeraars/9/bewerken/
Exception Value: 'Insurer' object has no attribute 'submittedprescription'
在这种情况下,

submittedprescription对保险公司有一个ForeignKey。我很可能以错误的方式解决这个问题,所以请赐教。

1 个答案:

答案 0 :(得分:1)

当对象实际上没有这种关系时,相关字段可能不会被设置为实例属性。

您最好使用后备值。例如:

if getattr(previous_state, field.name, None) != getattr(new_state, field.name, None):

作为替代方案,您可能还想要排除“一对多”关系:

fields = self.__class__._meta.get_fields(include_parents=False)
for field in fields:
    if not field.one_to_many and getattr(previous_state, field.name) != getattr(new_state, field.name):
        print('field {} changed'.format(field))

或排除不可编辑的字段:

fields = self.__class__._meta.get_fields(include_parents=False)
for field in fields:
    if field.editable and getattr(previous_state, field.name) != getattr(new_state, field.name):
        print('field {} changed'.format(field))