How to execute code on save in Django User model? - python

I would like to run some code specifically when the is_active field is changed for a Django User, similar to how the save method works for other models:
class Foo(models.Model):
...
def save(self, *args, **kwargs):
if self.pk is not None:
orig = Foo.objects.get(pk=self.pk)
if orig.is_active != self.is_active:
# code goes here
Can this be done through another model that is in one to one relation with the User model? Something like:
class Bar(models.Model):
owner = models.OneToOneField(User, on_save=?)
...
I guess I could duplicate the is_active field on the related model and then set the is_active field on the User when saving the related model. But this seems like a bit of a messy solution.

You're looking for this Signal
from django.db.models.signals import pre_save
from django.contrib.auth.models import User
def do_your_thing(sender, instance, **kwargs):
# Do something
print(instance)
pre_save.connect(do_your_thing, sender=User)

Related

OneToOne connection between User and Profile in Django

I am creating an app in django in which after user model is created, via the signal I create a profile:
from django.contrib.auth.models import User
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
But as far as I feel, this does not guarantees me that profile id always equals user's one
Is there any smart way to do this safer?
I think about creating a signal which will delete profile model after user one and opposite, but I still feel that not all cases are covered
When you are creating a separate model linked in such a way, you can add primary_key = True to your OneToOne field eg,
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
This will ensure you can use the same PK value for the User and the Profile.
It's not recommended to use the built-in django User model. It's better to create your own User model, that inherits from AbstractUser. Then override the save method:
from django.contrib.auth.models import AbstractUser
class MyUser(AbtractUser)
""" Add additional fields if you want """
def __str__(self):
return "something"
def save(self, *args, **kwargs):
Profile.objects.create(user=self)
super(MyUser, self).save(*args, **kwargs)
You can also override the delete() and other model methods, read the documentation about it here: https://docs.djangoproject.com/en/4.0/topics/db/models/#overriding-predefined-model-methods

Cannot modify a model field on save()

I'm a beginner with Django, and first time askig :)
I'm following a simple tutorial on generating a slug for a string (let's say a slug for a blog post generated from its title).
Perhaps I'm following an outdated guide, perhaps I'm missing a basic thing, I have no idea.
Django 2.0
Python 3.6
I am trying to do a very simple task of slugifying a simple string, so:
User enters a string in a simple form
When hitting 'save', the title goes through slugify and creates the
slug
Save.
models.py
from django.db import models
class Testmodel(models.Model):
title = models.CharField(max_length=220)
slug = models.SlugField(unique=True, null=True)
def __str__(self):
return self.title
views.py
from django.views.generic.edit import CreateView
class TestCreate(CreateView):
model = Testmodel
fields = '__all__'
forms.py
from django.forms import ModelForm
from .models import Testmodel
class TestCreateForm(ModelForm):
class Meta:
model = Testmodel
fields = '__all__'
Up until here everythig works, if I enter the slug manualy. In order to do it automaticaly, I have tried:
Overriding the save() method withing my ModelForm class.
Overriding the form_valid() method within the CreateView
Overriding the save() method within the model itself.
Tried to connect a pre_save signal to the model.
In all of these 4 tries, I had the same results:
When generating the form with the slug field, I couldn't do anything because it was required.
When generating the form without the slug field, nothing happens when I hit save.
The only way I have found to dodge this issue is to set the slug field to blank = True as well. I am not sure how secure it is, though?
Thank you!
Welcome to StackOverflow. You've written a wonderfuly constructed question (Cheers!)
When generating the form with the slug field, I couldn't do anything because it was required.
Okay so first we exlcude the slug because we want it to be autogenerated.
You can do this by
class TestCreateForm(ModelForm):
class Meta:
model = Testmodel
exclude = ['slug']
Now you'll get a form without the slug field.
When generating the form without the slug field, nothing happens when I hit save.
Now we override the save() function of the model itself since slug is a part of the model.
def save(self, *args, **kwargs):
self.slug = slugify(self.title)
super().save(*args, **kwargs)
But this will generate the slug everytime the model is saved.
We can go a step further and make sure the slug is set only if the model is 'created' and not every time it is 'updated'
def save(self, *args, **kwargs):
if not self.id:
self.slug = slugify(self.title)
super().save(*args, **kwargs)

Unable to autopopulate a Django admin field using an attribute from a OneToOne relationship

There are similar questions to this, but I believe mine is different. I am very new to Django and to Python, so please forgive my ignorance.
I have a custom class UserProfile that inherits from the django.contrib.auth.models User class. This UserProfile is based on the exercise in Tango with Django, however, I am using the example to create a different project/app.
I have UserProfile linked to the standard User model with a OneToOneField relationship in my models.py, as shown below:
class UserProfile(models.Model):
# Links UserProfile to a User model instance.
user = models.OneToOneField(User)
# The additional attribute I wish to include that isn't in User.
slug = models.SlugField(unique=True)
In my admin.py file, I want an interface for UserProfile that I can work with, and I want the slugfield to autopopulate when I enter a new UserProfile. I want it to autopopulate based on the username attribute of User. However, I can't seem to make it work. Here is my admin.py code:
class UserProfileAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("user.username",)}
When I try to runserver from my command line, I get the following error:
ERRORS: <class 'climbcast.admin.UserProfileAdmin'>: (admin.E030) The
value of >'prepopula ted_fields["slug"][0]' refers to 'user.username',
which is not an attribute of >' climbcast.UserProfile'.
System check identified 1 issue (0 silenced).
It won't allow me to access the user.username attribute this way, even though I can access it that way in the python shell. Any ideas on how to make this work?
Unfortunately prepopulated_fields doesn’t accept DateTimeField, ForeignKey, nor ManyToManyField fields.
Source: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.prepopulated_fields
Possible solution, in your models.py (make slug attribute optional):
from django.utils.encoding import force_text
from django.template.defaultfilters import slugify
class UserProfile(models.Model):
[...]
slug = models.SlugField(blank=True, db_index=True, unique=True)
def get_unique_slug(self, value):
"""
Generate a valid slug for a for given string.
"""
qs = self.__class__.objects.all()
used = qs.values_list('slug', flat=True)
i = 1
baseslug = slugify(value)
while slug in used:
slug = '%s-%s' % (baseslug, i)
i += 1
return slug
def save(self, *args, **kwargs):
if not self.slug and self.user:
self.slug = self.get_unique_slug(force_text(self.user.username))
super(UserProfile, self).save(*args, **kwargs)

Add a field to the ModelAdmin Form

I want to give users the possibility to create multiple events at once. Therefore I would like to add a field to the admin-add-page where a number of repetitions can be specified. Then I want to override the save function and create multiple events (based on the input). I started writing some code but the admin add page does not update at all. I will show you the code below:
In admins.py:
class EventAdmin(admin.ModelAdmin):
form = EventForm
admin.site.register(Event, EventAdmin)
In forms.py
from django import forms
from django.db import models
from calendar_app.models import Event
class EventForm(forms.ModelForm):
name = models.CharField(max_length=100) # just for testing purpose
class Meta:
model = Event
def __init__(self, *args, **kwargs):
super(EventForm, self).__init__(*args, **kwargs)
if not kwargs.has_key('instance'):
self.fields['name'] = forms.CharField(label='Name')
self.base_fields['name'] = forms.CharField(label='Name')
def save(self, commit=True):
model = super(EventForm, self).save(commit=False)
# Save all the fields...
if commit:
model.save()
return model
But the "name" field is not showing up when I add an event. Any ideas? Thanks!
I used models.CharField instead of forms.CharField. See comments.

Creating & Updating UserProfiles in Django

Django newbie here stumbling my way around the docs. I'm trying to create a user profile using Django's "UserProfiles", but I'm having a little trouble with figuring out the proper way to set the code based on Django docs.
Here's my code, based on the docs. (The create_user_profile is 100% from the docs).
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User)
location = models.CharField(max_length = 100)
website = models.CharField(max_length=50)
description = models.CharField(max_length=255)
fullName = models.CharField(max_length=50)
email = models.EmailField(max_length = 100, blank = False)
created = models.DateTimeField(auto_now_add=True)
private = models.BooleanField()
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
What's the -proper- way to set and save these fields?
For example, if I have both the User and UserProfile models in one form (in a registration form, for example), how would I first create, then update all of this, before finally saving?
how would I first create, then update all of this, before finally saving
These aren't separate steps. When you create or update a record in Django, you are saving it to the database.
For the registration form, I'd recommend you set it up as a ModelForm on User records, then specify the additional fields you want to save to the profile and save them separately in the save function, like so...
class RegistrationForm(forms.ModelForm):
location = forms.CharField(max_length=100)
# etc -- enter all the forms from UserProfile here
class Meta:
model = User
fields = ['first_name', 'last_name', 'email', and other fields in User ]
def save(self, *args, **kwargs):
user = super(RegistrationForm, self).save(*args, **kwargs)
profile = UserProfile()
profile.user = user
profile.location = self.cleaned_data['location']
# and so on with the remaining fields
profile.save()
return profile
You could call profile.user.save() and after it profile.save() when you need to save data from registration form.

Categories