Установить и не дать изменить значение внешнего ключа (пользователь)

У меня есть 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() & нет, спасибо!

Вернуться на верх