我正在尝试创建一个跟踪对象/实例历史记录的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。我很可能以错误的方式解决这个问题,所以请赐教。
答案 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))