How to save the user who create/modify a record.

Hi Team,
I am a rookie on Django world, and I am facing with the following problem, and after spending many hours trying to figure out a solution browsing the internet I came here to ask for helping.
Basically, I need to know how to capture the user who create a record and save it in created_by field on DB, and of course I need to do the same when any user modified a existing record and save in last_modified_by field.

I have this model named Member on models.py


from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
#from django_currentuser.middleware import get_current_authenticated_user

class Member(models.Model):
    firstname = models.CharField(max_length=50)
    lastname = models.CharField(max_length=50)
    phone = models.IntegerField(null=True)
    joined_date = models.DateField(null=True)
    slug = models.SlugField(default="", null=False)
    created_dt = models.DateField(auto_now_add=True, null=True)
    last_modified_dt = models.DateField(auto_now=True, null=True)  # Automatically set on every update
    #created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name='created_by')
    #updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name='updated_by')

    created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='created_by')
    updated_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='updated_by')


    def __str__(self) -> str:
        return f"{self.firstname} {self.lastname}"
    

    def save(self, *args, **kwargs):
        # Set the author to the current user before saving
        if not self.pk:  # Check if it's a new instance
            self.created_by = kwargs.pop('User', None)
        super().save(*args, **kwargs)

on admin.py I have

from django.contrib import admin
from django.forms import ModelForm
from django.http import HttpRequest
from .models import Member


# Register your models here.

#@admin.register(Member)
class MembersAdmin(admin.ModelAdmin):
    list_display = ("firstname", "lastname", "joined_date")
    prepopulated_fields = {"slug": ("firstname", "lastname")}


admin.site.register(Member, MembersAdmin)

I tried to use something named serializers but it did not work either.

So, when I checked the admin session on the web, I found the fields “created_by” and “last_mofidied_by” but instead of capure the logged user, and save it, the field is shown as a dropdown listing the two users I have created.

I tried different things, but I am still not able to find out how to implement that on django

If you need further information about views.py or settings.py please let me know.

Welcome @lsaavedra21 !

Side Note: When posting code here, enclose the code between lines of three backtick - ` characters. This means you’ll have a line of ```, then your code, then another line of ```. This forces the forum software to keep your code properly formatted. (I’ve taken the liberty of modifying your original post for this. Please remember to do this in the future.)

Are you talking about doing this when using the admin to update data or in the general case?

If you’re talking about doing this in the admin, see the docs for the ModelAdmin save_model method.

If you’re talking about this in the general case, the same idea applies. The currently-authenticated user object is accessible in the request.user attribute. You can assign that value to a field of your model in a view.

In neither case do you want to have those fields as fields in your forms - you don’t want them to be user-selectable.

1 Like

Hi @KenWhitesell

thx for your reply, I will read the info about ModelAdmin save_model_method you kindly shared with me.

About the comment related to “you don’t want them to be user-selectable” you are totally right, I don’t want that, but the issue is, I don’t know how/why they became to selectable dropdown on admin section in web app (as shown in image I shared), either I don’t know hot to change. They must be read-only field (or just not to be shown) on web app. I just want to save/update them with user logged in when a record is created or modify.

Hi Team,

I tried to read/done what is mentioned on The Django admin site | Django documentation | Django

about how to save the current user into a field named created_by. (as mentioned, this is be able to track what user create a record, off course the idea is to have another field last_modified_by to see who was the last user to edit any record)

but I am still no able to do it, reading the guide on django website I supposed to do this:

so, I added the following code on my admin.py

from django.contrib import admin
from django.forms import ModelForm
from django.http import HttpRequest
from .models import Member


# Register your models here.

@admin.register(Member)
class MembersAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        obj.created_by =  request.user
        return super().save_model(request, obj, form, change)


    #list_display = ("firstname", "lastname", "joined_date")
    prepopulated_fields = {"slug": ("firstname", "lastname")}


but still the field on the web is showing up as a dropdown with all the users I have created on database. and of course, the record does not sabe automatically the user who is logged in.
by the way on the code line "obj.created_by = request.usr, the created_by is the field on my model Member where I wanted to save the user logged in.

kindly,
Luis Saavedra

Do not include that field in your ModelAdmin class for that model.

Thanks for replying to me @KenWhitesell

But still not working

ModelAdmin cass as follow

from django.contrib import admin
from django.forms import ModelForm
from django.http import HttpRequest
from .models import Member




@admin.register(Member)
class MembersAdmin(admin.ModelAdmin):
    def save_model(self, request, obj, form, change):
        #obj.created_by =  request.user
        return super().save_model(request, obj, form, change)

    #list_display = ("firstname", "lastname", "joined_date")
    prepopulated_fields = {"slug": ("firstname", "lastname")}

but still field created_by is showed as dropdown and no save it the current user logged int.

–
I just added a new Member, but not save the logged user into field created_by

This is not what I was referring to.

See the docs for ModelAdmin.fields

When you define a ModelAdmin class, by default, it’s going to include every field in the model.

In this specific case, you do not want to include the created_by field in the list of fields for that ModelAdmin class.

You can use the exclude method to remove that one field from that class.

1 Like

To prevent specific fields from being modified when creating an object.
don’t forget makemigartions and migrate after change model.

# models.py
class Member(models.Model):
  created_by = models.ForeignKey(
    User, on_delete=models.CASCADE, null=True, blank=True, related_name='created_by',
    editable=False, # add
  )
  updated_by = models.ForeignKey(
    User, on_delete=models.CASCADE, null=True, blank=True, related_name='updated_by',
    editable=False, # add
)

To record the user who created or changed an object

# admin.py
@admin.register(Member)
class MembersAdmin(admin.ModelAdmin):
  list_display = ("firstname", "lastname", "joined_date", 'created_by', 'updated_by')

  def save_model(self, request, obj, form, change):
    if not change:
      obj.created_by =  request.user
    else:
      obj.updated_by = request.user
    return super().save_model(request, obj, form, change)
1 Like

Dear @KenWhitesell & @white-seolpyo

Thanks a lot for your help, I was able to accomplish the task, now the created_by and updated_by field are automatically populated from user logged in.

Your help was very useful, and I really appreciated. thanks for sharing your knowledge.
Now, I have a best understanding on how to use admin.models.

Everything you mentioned was correct, I had a mistake on models Members.

I had this piece of code uncommented on my code:

def save(self, *args, **kwargs):
        # Set the author to the current user before saving
        if not self.pk:  # Check if it's a new instance
            self.created_by = kwargs.pop('User', None)
        super().save(*args, **kwargs)

so after commented it, it works, so at the end my models.Members is:

from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
#from django_currentuser.middleware import get_current_authenticated_user



# Create your models here.

class Member(models.Model):
    firstname = models.CharField(max_length=50)
    lastname = models.CharField(max_length=50)
    phone = models.IntegerField(null=True)
    joined_date = models.DateField(null=True)
    slug = models.SlugField(default="", null=False)
    created_dt = models.DateField(auto_now_add=True, null=True)
    last_modified_dt = models.DateField(auto_now=True, null=True)  # Automatically set on every update
    #created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name='created_by')
    #updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL, related_name='updated_by')

    created_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='created_by') # add)
    updated_by = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name='updated_by') # add) , editable=False


    def __str__(self) -> str:
        return f"{self.firstname} {self.lastname}"
    
'''
    def save(self, *args, **kwargs):
        # Set the author to the current user before saving
        if not self.pk:  # Check if it's a new instance
            self.created_by = kwargs.pop('User', None)
        super().save(*args, **kwargs)
        
'''



Changing save arg is not recommended.
If you really have to use it, it would be better to do it this way.

def save(self, User=None, *args, **kwargs):
        super().save(*args, **kwargs)

thank you for advising, indeed I wont use it, so I commented on my code that piece of code.
thanks a lot for your time to helping me