Установить и не дать изменить значение внешнего ключа (пользователь)
У меня есть 2 модели, для которых я хочу задать значение и в идеале сделать его скрытым от администратора Django при создании новой записи, таким образом пользователь не сможет изменить это значение. Это созданные и измененные записи, которые являются внешними ключами для пользователей.
Я нашел эту ссылку https://pypi.org/project/django-currentuser/, я думал, что она может сделать эту работу, однако она вернула мой django с последней версии на версию 3, поэтому я не хочу использовать ее, а также, она не работает, если я устанавливаю либо created, либо last modified, но не оба, если я устанавливаю это в двух моделях, я получаю 4 ошибки.
Мне интересно, есть ли простой способ установить это значение по умолчанию?
from django.db import models
from email.policy import default
from django.contrib.auth.models import User
from django.utils import timezone
# from django.contrib import admin
# https://pypi.org/project/django-currentuser/
from django_currentuser.middleware import (get_current_user, get_current_authenticated_user)
from django_currentuser.db.models import CurrentUserField
class Company(models.Model):
modified_by = models.ForeignKey(User, related_name='company_modified_by', unique = False, on_delete=models.CASCADE)
created_by = CurrentUserField()
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=150, unique = True)
class Meta:
verbose_name = "Company"
verbose_name_plural = "Companies"
def __str__(self):
return self.name
class UserProfile(models.Model):
modified_by = models.ForeignKey(User, related_name='user_profile_modified_by', unique = False, on_delete=models.CASCADE)#CurrentUserField(on_update=True)
created_by = CurrentUserField()
created_date = models.DateTimeField(auto_now_add=True)
modified_date = models.DateTimeField(auto_now_add=True)
Вы можете использовать editable=False, например,
modified_by = models.ForeignKey(User, related_name='company_modified_by', unique = False, on_delete=models.CASCADE, editable=False)
Согласно docs, если False, поле не будет отображаться в админке или любой другой ModelForm. Они также пропускаются при валидации модели. По умолчанию - True.
Таким образом, вы можете задать его программно во время создания (например, через запрос к представлению) и не беспокоиться о том, что он будет отредактирован.
def create_view(request):
if request.method == "POST":
company_form = CompanyForm(request.POST)
company_form.instance.created_by = request.user
company_form.save()
(Также - не забывайте, использовать
)modified_date = models.DateTimeField(auto_now=True)
для измененных дат. auto_now_add - для одноразовых обновлений создания.)
Я узнал, что instance
не работает так, как описано в предыдущем общении на stackoverflow. Я немного повозился и выяснил, как сделать мой обычный флаг is_edit в админке
Вот что я придумал. Это требует изменения admin.py и добавления новой формы. Значения все равно будут отображаться в таблице на странице администратора, что, я полагаю, хорошо, они просто скрыты в новой+редактируемой форме.
- Примечание: Я сделал только Компанию, так как я не уверен на 100% в том, как работает UserProfile, так как только два поля должны быть скрытыми, так что что же редактировать?
admin.py
from django.contrib import admin
# this \/ needs to change
from myapp.forms import CompanyForm
from myapp.models import Company
class CompanyAdmin(admin.ModelAdmin):
# columns to show in admin table
list_display = (
'name',
'created_by', 'created_date',
'modified_by', 'modified_date',
)
# custom form
form = CompanyAdminForm
# override default form_save to pass in the request object
# - need request.user inside the save method for `{x}_by = user`
def save_form(self, request, form, change):
return form.save(commit=False, request=request)
admin.site.register(Company, CompanyAdmin)
forms.py
from django import forms
from django.contrib import admin
# this \/ needs to change
from myapp.models import Company
class CompanyForm(forms.ModelForm):
class Meta:
model = Company
fields =['name']
exclude =['modified_by', 'created_by', 'created_date', 'modified_date']
def __init__(self, *args, **kwargs):
# We must determine if edit here, as 'instance' will always exist in the save method
self.is_edit = kwargs.get('instance') != None
super(CompanyForm, self).__init__(*args, **kwargs)
def save(self, commit=True, *args, **kwargs):
# We must Pop Request out here, as super() doesn't like extra kwargs / will crash
self.request = kwargs.pop('request') if 'request' in kwargs else None
obj = super(CompanyForm, self).save(commit=False, *args, **kwargs)
# do your stuff!
from datetime import datetime
if self.is_edit:
obj.modified_date = datetime.now().date()
obj.modified_by = self.request.user
else:
obj.created_date = datetime.now().date()
obj.created_by = self.request.user
if commit:
obj.save()
return obj
Примечание: Но вы можете повторно использовать форму Company для формы non-admin
, вам просто нужно не забыть вызвать сохранение, как: form.save(commit=False, request=request)
# Example (Might need some minor tinkering)
def myview(request):
if request.method == 'POST':
form = CompanyForm(request.POST)
if form.is_valid():
form.save(commit=True, request=request)
Обычно я ввожу vars в объявление + __init__
, ex form = CompanyForm(request.POST, request=request, is_edit=True)
вместо save(), но 1 взгляд на contrib/admin/options.py
+ ModelAdmin.get_form()
& нет, спасибо!