I have 2 models that I want to preset the value and ideally have it hidden from the Django admin when creating new record, this way the user don't amend this value. This are the created and modified by that are foreign keys to users.
I found this link https://pypi.org/project/django-currentuser/, that i thought it might do the job, however it reverted my django from the latest version to version 3, so I dont want to use it, and also, it doesnt work if i set either created or last modified but not both, if i set it in the 2 models i get 4 errors.
I am wondering if there is an easy way to set this default value?
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)
CodePudding user response:
You can use editable=False, eg,
modified_by = models.ForeignKey(User, related_name='company_modified_by', unique = False, on_delete=models.CASCADE, editable=False)
According to the docs, If False, the field will not be displayed in the admin or any other ModelForm. They are also skipped during model validation. Default is True.
That way, you can set it programmatically during creation (eg, via a request to a view) and not have to worry about it being edited.
def create_view(request):
if request.method == "POST":
company_form = CompanyForm(request.POST)
company_form.instance.created_by = request.user
company_form.save()
(Also - don't forget, use
modified_date = models.DateTimeField(auto_now=True)
for modified dates. auto_now_add is for one time creation updates.)
CodePudding user response:
I've learned that instance
doesn't work as described in a previous stackoverflow interaction. I've done some tinkering figured out how to do my usual is_edit flag in the admin
This is what I've come up with. It requires changing the admin.py and adding a new form. The values will still show up in that table in the admin page, which I assume is good, they're just hidden in the new edit forms.
- Note: I only did Company as I'm not 100% sure on how UserProfile works as the only two fields are supposed to be hidden ones, so what's to edit?
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
Note: But you can reuse the Company form for a non-admin
form, you just have to remember to call the save like: 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)
Normally I inject vars into the declaration __init__
, ex form = CompanyForm(request.POST, request=request, is_edit=True)
instead of the save() but 1 look at contrib/admin/options.py
ModelAdmin.get_form()
& no thanks!