I would like after creating a user to run a method and create content in other tables of this user that was created but I do not have a user model since I use the default model for the record.
Problems:
-I do not have user model (use the default model)
-I want to choose the newly created user to create the other records in other tables
-I'm not an expert in django (I'm just trying to solve a problem in another area and this would help)
I like the way to do it in this post:
https://simpleisbetterthancomplex.com/tutorial/2016/07/28/how-to-create-django-signals.html
Creating a signals.py to control the signals
You can attach a Django signal to the Django default User models with no problems, for example:
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
The instance argument is the new user recently created into the database, so you can use it as a reference to to add the new tables.
As well, you can put this function in your models.py as well, not necessarily need to create a signals.py
Related
I have 3 database tables:
users (stores info about users e.g. email, name)
metadata (stores data)
activity (stores changes made to users/metadata tables)
I want to achieve the following:
to store any change into the activity table (e.g. new user is created, a user updates the metadata table)
to send notifications to users whenever a change into the users/metadata tables happens.
What are the libraries/method that I could use in order to achieve the desired functionalities? Thank you!
in addition to django signals which wes already recommended, you can also check out django channels + django activity stream
Assuming you are making use of the Django Models, I would use the Django Signals for saving the data to a second model: "MetaData" according to your question.
Specifically the post_save signal:
https://docs.djangoproject.com/en/4.0/ref/signals/#django.db.models.signals.post_save
#receiver(post_save, sender=User)
def post_save_user(sender, instance, created, **kwargs):
# Save Stuff To Metadata Model.
meta = MetaData(user=instance)
meta.save()
You need to import those signals inside your models.py placed at the bottom of the file.
As for the notifications, I would follow #kristina's advice and use Django Channels.
Just remind that your application needs to be ASGI (Async). So your application server needs to be one like uvicorn, daphne, hypercorn, etc.
Say for example I have a Model called Player. I want Model objects of Player created when new Users are added in django (from django.contrib.auth.models.User) such that each User object in User model has its own object in Player model. I know that I would technically have to use models.ForeignKey() to create a field in Player model and relate it to the User model and that I don't have to worry about deletion of users if I use the on_delete=models.CASCADE parameter in models.ForeignKey() but how do I automatically create these objects with like default values and such.
Initially this was my code to do so:
for name in User.objects.all().values_list('username', flat=True):
if name not in Player.objects.all().values_list('player_name', flat=True):
new_player = Player(player_name=name, current_level_no=None, no_of_moves=None)
new_player.save()
else:
pass
But this would give me a DatabaseIntegrityError that "Models aren't loaded yet". So I am confused about what to do or how to go forward.
As always, I greatly appreciate all answers!
Check out Django signals, specifically the post_save signal. Essentially, Django lets you define a function that will be run whenever your User model is saved. You'd use it something like this:
from django.db.models.signals import post_save
def create_player(sender, instance, created, **kwargs):
if created:
Player.objects.create(user=instance) # instance is the User object
post_save.connect(create_player, sender=User)
Note that if you already have User objects in your database, this won't create Player objects for them; you'll want to do that by running a loop similar to the one you posted in the Django shell (using python manage.py shell). The signal will run on every subsequent save of a user model.
I created a 'profile' model (with a 1-to-1 relationship to the User model) as described on Extending the existing user model. The profile model has an optional many-to-one relationship to another model:
class Profile(models.Model):
user = models.OneToOneField(User, primary_key=True)
account = models.ForeignKey(Account, blank=True, null=True, on_delete=models.SET_NULL)
As documented there, I also created an inline admin:
class ProfileInline(admin.StackedInline):
model = Profile
can_delete = False
verbose_name_plural = 'profiles'
# UserAdmin and unregister()/register() calls omitted, they are straight copies from the Django docs
Now if I don't select an account in the admin when creating the user, the profile model won't be created. So I connect to the post_save signal, again just following the documentation:
#receiver(post_save, sender=User)
def create_profile_for_new_user(sender, created, instance, **kwargs):
if created:
profile = Profile(user=instance)
profile.save()
This works fine as long as I do not select an account in the admin, but if I do, I'll get an IntegrityError exception, telling me that duplicate key value violates unique constraint "app_profile_user_id_key" DETAIL: Key (user_id)=(15) already exists.
Apparently, the inline admin tries to creates the profile instance itself, but my post_save signal handler has already created it at that time.
How do I fix this problem, while keeping all of the following requirements?
No matter how the new user is created, there will always be a profile model linking to it as well afterwards.
If the user selects an account in the admin during user creation, this account will be set on the new profile model afterwards. If not, the field is null.
Environment: Django 1.5, Python 2.7
Related questions:
Creating a extended user profile (similar symptoms, but the cause turned out to be a different one)
The problem can be avoided by setting primary_key=True on the OneToOneField pointing at the User model, as you have figured out yourself.
The reason that this works seems to be rather simple.
When you try to create a model instance and set the pk manually before saving it, Django will try to find a record in the database with that pk and update it rather than blindly attempting to create a new one. If none exists, it creates the new record as expected.
When you set the OneToOneField as the primary key and Django Admin sets that field to the related User model's ID, that means the pk is already set and Django will attempt to find an existing record first.
This is what happens with the OneToOneField set as primary key:
Django Admin creates the new User instance, with no id.
Django Admin saves the User instance.
Because the pk (in this case id) is not set, Django attempts to create a new record.
The new record's id is set automatically by the database.
The post_save hook creates a new Profile instance for that User instance.
Django Admin creates the new Profile instance, with its user set to the user's id.
Django Admin saves the Profile instance.
Because the pk (in this case user) is already set, Django attempts to fetch an existing record with that pk.
Django finds the existing record and updates it.
If you don't set the primary key explicitly, Django instead adds a field that uses the database's auto_increment functionality: the database sets the pk to the next largest value that doesn't exist. This means the field will actually be left blank unless you set it manually and Django will therefore always attempt to insert a new record, resulting in a conflict with the uniqueness-constraint on the OneToOneField.
This is what causes the original problem:
Django Admin creates the new User instance, with no id.
Django Admin saves the User instance, the post_save hook creating a new Profile instance as before.
Django Admin creates the new Profile instance, with no id (the automatically added pk field).
Django Admin saves the Profile instance.
Because the pk (in this case id) is not set, Django attempts to create a new record.
The database reports a violation of the table's uniqueness-constraint on the user field.
Django throws an Exception. You will not go to space today.
It seems like setting primary_key=True on the OneToOneField connecting the profile model to the User model fixes this issue. However, I don't think I understand all the implications of that and why it helps.
I'll leave this here as a hint, but if that's the best solution and someone could come up with a well-written explanation, I'd upvote/accept that and possibly delete mine.
I've installed django-userena to manage user profiles and everything works fine except new registered users unable to edit/update their profiles and face just blank screen.
If I make the user a superuser then it can change/update profile.
Found that profile_edit view in django-userena decorated with #permission_required_or_403('change_profile', (get_profile_model(), 'user__username', 'username'))
Obviously need to add post_save signal to add necessary permission and nevertheless I was wondering if there any settings like USERENA_ALLOW_UPDATE_PROFILE can anyone help me on this?
Finally digging around django-userena and django-guardian sources I present my output of this little research, so if you want the users to be able to edit their profile you can use the following code
User post save signal which adds 'change_profile' permission to new user objects
#receiver(post_save, sender=User, dispatch_uid='user.created')
def user_created(sender, instance, created, raw, using, **kwargs):
""" Adds 'change_profile' permission to created user objects """
if created:
from guardian.shortcuts import assign
assign('change_profile', instance, instance.get_profile())
For existing users, Just issue this command and the page will be working:
python manage.py check_permissions
I'm rolling my own custom registration module in Django based on django.contrib.auth. My registration module will have some extra functionality and help me reduce my dependency on other django modules that I'm currently using like django-registration and django-emailchange. I've run into a what-the-best-way-to-do-it problem here.
Note: All the user accounts are based on django.contrib.auth.models.User model.
When the user clicks the "sign-up" link, the request gets passed to my view named register. I have a custom form which has four fields — username, email, password1 and password2. The form is based on django.forms.Form. The form provides basic validation e.g. passoword1 and password2 are the email; the email/username do not exist.
When the data gets POSTed back to my register view, I call the is_valid() method of the form, after which, I create a new user by calling a Manager method called create_user() in django.contrib.auth.models.UserManager. I need to add more custom functionality at this point like sending activation emails, etc. As a best-practice method, where should this logic be? Should this be in a method of the User Model? Should it be where it is currently - the Manager of the Model? Or should this be placed into a custom save() method of my sign-up Form?
Thanks.
Different from Chris, I believe on the philosophy of fat models, thin views.
The more code you can factor inside models, the more reusable your codebase is. Views concerns should be simply managing the request/response cycle and dealing with GET/POST parameters.
In this case, sending activation emails is related to the event of creating a new User. For those cases, Django already provides this abstraction through signals.
http://docs.djangoproject.com/en/1.2/topics/signals/#topics-signals
So, as an example, you can have in your models.py:
from django.contrib.models import User
from django.db.models.signals import post_save
def send_welcome_email(self):
# Reusable email sending code
User.send_welcome_email = send_welcome_email
def welcome_emails(sender, instance, created, **kwargs):
if created:
instance.send_welcome_email() # `instance` is User
post_save.connect(welcome_emails, sender=User)
Similarly, you can have this for when a User is deleted, or everytime a User is saved, etc. Signals are a good abstraction to event-driven tasks.
My recommendation is to not re-solve a problem that django-registration solves quite nicely. It has a pluggable backend system that lets you customize it as much or as little as needed.