Django - Create user profile on user creation - django

I'm following Django documentation here in order to achieve a simple objective: Create a user profile as soon as a new user is created.
I have an 'accounts' app and my accounts.models looks like this:
# -*- coding: utf-8 -*-
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from main.models import Store
class UserProfile(models.Model):
GENRE_CHOICES = (
('m', 'Masculino'),
('f', 'Feminino'),
)
MARITAL_STATUS_CHOICES = (
('s', 'Solteiro'),
('c', 'Casado'),
('d', 'Divorciado'),
('v', 'Viúvo'),
)
user = models.ForeignKey(User, unique=True)
birth_date = models.DateField()
genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
address = models.CharField(max_length=150)
postal_code_4 = models.PositiveIntegerField()
postal_code_3 = models.PositiveIntegerField()
locatity = models.CharField(max_length=30)
marital_status = models.CharField(max_length=1, choices=MARITAL_STATUS_CHOICES)
child_amount = models.PositiveSmallIntegerField()
is_merchant = models.BooleanField(default=False)
store = models.ForeignKey(Store, null=True)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
Everything looks fine to me but when trying to add a new user (using django admin), instead of having a newly created user and user profile, I get the following error:
InternalError at /admin/auth/user/add/
current transaction is aborted, commands ignored until end of transaction block
Here is the traceback error part:
/djangoProjects/lwboanova/lwboanova/apps/accounts/models.py in create_user_profile
34: UserProfile.objects.create(user=instance)
It seems like an integrity error but I'm not getting the reason for it.
Would be great if any of ya could give me some help on this.

You shouldn't use:
user = models.ForeignKey(User, unique=True)
Instead use this:
from django.conf import settings
..
user = models.OneToOneField(settings.AUTH_USER_MODEL)

Just figured it out.
I forgot to add null=True to the rest of UserProfile model fields.
So the accounts.models.UserProfile fields now looks like:
user = models.ForeignKey(User, unique=True)
birth_date = models.DateField(null=True)
genre = models.CharField(max_length=1, choices=GENRE_CHOICES, null=True)
address = models.CharField(max_length=150, null=True)
postal_code_4 = models.PositiveIntegerField(null=True)
postal_code_3 = models.PositiveIntegerField(null=True)
locatity = models.CharField(max_length=30, null=True)
marital_status = models.CharField(max_length=1, choices=MARITAL_STATUS_CHOICES, null=True)
child_amount = models.PositiveSmallIntegerField(null=True)
is_merchant = models.BooleanField(default=False)
store = models.ForeignKey(Store, null=True)
...and everything is working as intended!
Cheers for trying to help Ashray ^^

You can create this using Django Signals. Reference https://docs.djangoproject.com/en/2.2/topics/signals/
Add this to installed_apps in settings.py
# settings.py
INSTALLED_APPS = [
...
'accounts.apps.AccountsConfig',
...
]
Note: if you use accounts in INSTALLED_APPS, you'll have to initialise in init.py
In models.py remove create_user_profile
# models.py
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from main.models import Store
class UserProfile(models.Model):
GENRE_CHOICES = (
('m', 'Masculino'),
('f', 'Feminino'),
)
MARITAL_STATUS_CHOICES = (
('s', 'Solteiro'),
('c', 'Casado'),
('d', 'Divorciado'),
('v', 'Viúvo'),
)
user = models.OneToOneField(User)
birth_date = models.DateField()
genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
address = models.CharField(max_length=150)
postal_code_4 = models.PositiveIntegerField()
postal_code_3 = models.PositiveIntegerField()
locatity = models.CharField(max_length=30)
marital_status = models.CharField(max_length=1, choices=MARITAL_STATUS_CHOICES)
child_amount = models.PositiveSmallIntegerField()
is_merchant = models.BooleanField(default=False)
store = models.ForeignKey(Store, null=True)
Add create_user_profile in signals.py
# signals.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import UserProfile
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
Finally, edit apps.py
# apps.py
from django.apps import AppConfig
class AccountsConfig(AppConfig):
name = 'accounts'
def ready(self):
import accounts.signals

def create_profile(sender,**kwargs ):
if kwargs['created']:
user_profile=UserProfile.objects.create(user=kwargs['instance'])
post_save.connect(create_profile,sender=User)
I think this will help you.

#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
Like this it will be easy to read code.

#receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()

#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if kwargs.get('created', True) and not kwargs.get('raw', False):
Profile.objects.create(user=instance)

I had success trying this method
def create_profile(sender,**kwargs ):
if kwargs['created']:
user_profile=UserProfile(user=kwargs['instance'])
user_profile.save()
post_save.connect(create_profile, sender=User)

Related

django custom save insert to multiple table

I have an Item table and TransactionLog table, When I create an item it should also create a log
this is my models.py
class Item(models.Model):
itemid = models.CharField(primary_key=True, max_length=20)
name = models.CharField(max_length=100, blank=True, null=True)
class Meta:
managed = False
db_table = 'items'
def save(self, *args, **kwargs):
TransactionLog.objects.create(itemid=self.cl_itemid,
trans_desc="Add Clearance Item",
trans_recorded=timezone.now())
super(Item, self).save(*args, **kwargs)
class TransactionLog(models.Model):
log_id = models.AutoField(primary_key=True)
itemid = models.ForeignKey('Item', models.DO_NOTHING, db_column='itemid', blank=True, null=True)
trans_desc = models.TextField(blank=True, null=True)
trans_recorded = models.DateField(blank=True, null=True)
class Meta:
managed = False
db_table = 'transaction_log'
but When I try to insert it's saying Cannot assign "'CL-20221106-0000'": "TransactionLog.itemid" must be a "Item" instance. maybe because the Item isnt created yet?
My Suggestion is Use Signal insted of savind data on save method.
#signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from yourapp.models import Item, TransactionLog
#receiver(post_save, sender=Item)
def create_transaction_log(sender, instance, created, **kwargs):
if created:
TransactionLog.objects.create(itemid=instance.cl_itemid,
trans_desc="Add Clearance Item",trans_recorded=timezone.now())
# apps.py
from django.apps import AppConfig
class YourAPPConfig(AppConfig):
name = 'yourappname'
def ready(self):
import yourapp.signals

Django: Is it Possible to create a model like Profile Model, to create an instance at the time of registration automatically

I have a custom user model and has created a profile model from it as well. so when user sign up a profile instance is created in the profile model as well. Now I have another similar model which is the address model. I tried configuring it in the same way but the address instance isn't getting created. Is it possible to do that? This is just for an understanding, whether similar model like profile can be created.
this is my model.
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
summary = models.TextField(max_length=250, blank=True, null=True)
birth_date = models.DateField(null=True,blank=True, auto_now_add=False, auto_now=False)
country = CountryField(blank_label='(select country)')
profile_pic = models.ImageField(upload_to='pimage/', default='pimage/default.png')
def __str__(self):
return f'{self.user.username} Profile'
class Address(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
product = models.ForeignKey(Product, on_delete=models.CASCADE, blank=True, null=True)
address = models.CharField(max_length=200, null=True)
city = models.CharField(max_length=200, null=True)
state = models.CharField(max_length=200, null=True)
zipcode = models.CharField(max_length=200, null=True)
mobile = models.CharField(max_length=12, null=True)
def __str__(self):
return f'{self.user.username} Address'
views
#login_required
def address_add(request):
if request.POST:
a_form = AddressForm(request.POST, instance=request.user.address)
if a_form.is_valid():
a_form.save()
messages.success(request, f'Address Saved')
return redirect('user_address')
else:
a_form = AddressForm(instance=request.user.address)
context = {'a_form':a_form,}
return render(request, 'áccounts/user_address.html', context)
You can override save() method of User model to create an Address instance automatically:
from django.db import models
class User(models.Model):
# Your user model fields
def save(self, *args, **kwargs):
if self._state.adding:
# Create your Address instance here
pass
super(User, self).save(*args, **kwargs)
If you want to know how you can extend the Django user model here is a link that can help you.
Even though you can define some signal to create your Address instance after an User instance has been created.
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Address(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
#receiver(post_save, sender=User)
def create_user_address(sender, instance, created, **kwargs):
if created:
Address.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_address(sender, instance, **kwargs):
instance.address.save()

"Invalid field name(s) given in select_related" when searching one-to-many relationship

my apologies as I am new to Django and this seems like a "Django 101" type problem, but I just can't seem to get it to work. I have a Django model for "Services" which has a related model for "Keywords" via a one-to-many relationship. I'm simply trying to return the related keywords when I query the services, but when I run the view, I keep getting the error:
Invalid field name(s) given in select_related: 'keyword'. Choices are: (none)
My models are as follows:
from uuid import uuid4
from django.db import models
from django.utils.text import slugify
from blog.models import Tag, Category
class Service(models.Model):
id_service = models.UUIDField(primary_key=True, default=uuid4, editable=False)
created_ts = models.DateTimeField(auto_now_add=True)
updated_ts = models.DateTimeField(auto_now=True)
service_name = models.CharField(
db_index=True, max_length=50, blank=False, null=False
)
slug = models.CharField(max_length=50, unique=True)
font_awesome_icon = models.CharField(blank=True, null=True, max_length=30)
service_image = models.ImageField(
blank=True, null=True, upload_to="images/services", max_length=None
)
service_page_image = models.ImageField(
blank=True, null=True, upload_to="images/services", max_length=None
)
service_description = models.TextField(blank=False, null=False)
service_description_brief = models.CharField(
max_length=200, blank=False, null=False
)
rank = models.IntegerField()
def save(self, *args, **kwargs):
self.slug = slugify(self.service_name)
super(Service, self).save(*args, **kwargs)
def __str__(self):
return self.service_name
class ServiceKeyword(models.Model):
id_servicekeywords = models.UUIDField(
primary_key=True, default=uuid4, editable=False
)
created_ts = models.DateTimeField(auto_now_add=True)
updated_ts = models.DateTimeField(auto_now=True)
keyword = models.CharField(max_length=60, blank=False, null=False)
service = models.ForeignKey(Service, on_delete=models.CASCADE)
def __str__(self):
return self.keyword
And the view that is throwing the error is:
import random
import markdown2
import geoip2.database
import datetime
from django.views.generic.base import TemplateView
from django.views.generic.list import ListView
from django.core.paginator import Paginator
from django.http import Http404
from django.shortcuts import render
from django.urls import reverse
from services.models import Service, ServiceKeyword
class ServiceView(TemplateView):
template_name = "services/service.jinja"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
servicename = self.kwargs["servicename"]
service_list = Service.objects.select_related("keyword").order_by("rank")
context["service_list"] = service_list
context["faq_list"] = Post.objects.filter(categories__slug=servicename)
for faq in context["faq_list"]:
faq.content = markdown2.markdown(faq.content)
# Get service with that slug and take from list
service = service_list.filter(slug=servicename)[0]
context["keyword_list"] = service.keyword.all().order_by("?")[7]
context["service"] = service
)
return context
Any help from the pros would be greatly appreciated, as I've looked at the docs and spend an inordinate amount of time trying to fix. Thank you!
Defining a related_name on your field definition should solve your problem :
class ServiceKeyword(models.Model):
...
service = models.ForeignKey(Service, related_name='keyword', on_delete=models.CASCADE)
...
You can find the documentation for the related_name HERE

DJANGO createsuperuser not working...TypeError: create_superuser() missing 1 required positional argument: 'username'

I am trying to create a RESTful API using Django and DRF. I have a User model which extends AbstractUser. I'm neither able to create normal users nor a superuser. For some reason, It says
When I run:
python manage.py createsuperuser
I get the following error:
"TypeError: create_superuser() missing 1 required positional argument: 'username'"
Here's the models.py file:
import uuid
from django.db import models
from django.conf import settings
from django.dispatch import receiver
from django.contrib.auth.models import AbstractUser
from django.utils.encoding import python_2_unicode_compatible
from django.db.models.signals import post_save
from rest_framework.authtoken.models import Token
from api.fileupload.models import File
#python_2_unicode_compatible
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
profile_picture = models.ForeignKey(File, on_delete=models.DO_NOTHING, null=True, blank=True)
email = models.EmailField('Email address', unique=True)
name = models.CharField('Name', default='', max_length=255)
phone_no = models.CharField('Phone Number', max_length=255, unique=True)
company_name = models.CharField('Company Name', default='', max_length=255)
address = models.CharField('Address', default='', max_length=255)
address_coordinates = models.CharField('Address Coordinates', default='', max_length=255)
country = models.CharField('Country', default='', max_length=255)
pincode = models.CharField('Pincode', default='', max_length=255)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __str__(self):
return self.email
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create_user(user=instance)
The serializers.py file:
from django.contrib.auth.password_validation import validate_password
from rest_framework import serializers
from .models import User
from api.fileupload.serializers import FileSerializer
class UserSerializer(serializers.ModelSerializer):
profile_picture = FileSerializer()
def create(self, validated_data):
user = User.objects.create(**validated_data)
return user
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.company_name = validated_data.get('company_name', instance.company_name)
instance.address = validated_data.get('address', instance.address)
instance.country = validated_data.get('country', instance.country)
instance.pincode = validated_data.get('pincode', instance.pincode)
instance.phone_no = validated_data.get('phone_no', instance.phone_no)
instance.email = validated_data.get('email', instance.email)
instance.profile_picture = validated_data.get('profile_picture', instance.profile_picture)
instance.save()
return instance
class Meta:
unique_together = ('email',)
model = User
fields = (
'id', 'password', 'email', 'name', 'phone_no', 'company_name', 'address', 'country', 'pincode', 'profile_picture',
)
extra_kwargs = {'password': {'write_only': True}}
The views.py file:
from rest_framework import viewsets, mixins
from .models import User
from .serializers import UserSerializer
class UserViewSet(mixins.RetrieveModelMixin,
mixins.ListModelMixin,
mixins.CreateModelMixin,
viewsets.GenericViewSet):
queryset = User.objects.all()
permission_classes = (AllowAny,)
serializer_class = UserSerializer
When you use createsuperuser command, it will require you to input some required field, USERNAME_FIELD is a required field, and all fields inside REQUIRED_FIELDS. Your code set REQUIRED_FIELDS=[] so there is no required field, just need to add email and password.
But when createsuperuser has been called, it will call create_superuser method which require username field:
def create_superuser(self, username, email=None, password=None, **extra_fields):
so that your input data is not enough to pass the data into create_superuser method.
To solve this, you can simply just add username into REQUIRED_FIELDS:
REQUIRED_FIELDS = ['username']
then createsuperuser will require you to add username field for create_superuser method.
Hope that helps.

'UserProfile' object has no attribute 'id'

I have an update form, whem i press updated for the form to update the user profile it I get the error "'UserProfile' object has no attribute 'id'"
my models is:
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name="custom_user_profile", primary_key=True)
organization = models.CharField(max_length=100, default='', blank=True)
address_line_1 = models.CharField(max_length=100, default='', blank=True)
address_line_2 = models.CharField(max_length=100, default='', blank=True)
city = models.CharField(max_length=100, default='', blank=True)
state = models.CharField(max_length=100, default='', blank=True)
post_code = models.CharField(max_length=100, default='', blank=True)
country = models.CharField(max_length=100, default='', blank=True)
def __unicode__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
profile, new = UserProfile.objects.get_or_create(user=instance)
and my views is:
from django.shortcuts import render_to_response
from django.shortcuts import HttpResponseRedirect
from django.template import RequestContext
from django.core.context_processors import csrf
from django.contrib.auth.decorators import login_required
from user_profile.models import UserProfile
from django.contrib.auth.models import User
from forms import UserProfileForm
from django.shortcuts import redirect
#login_required
def update_profile(request):
userProfile = UserProfile.objects.get(user=request.user)
form = UserProfileForm(initial={
'organization':userProfile.organization,
'address_line_1':userProfile.address_line_1,
'address_line_2':userProfile.address_line_2,
'city':userProfile.city,
'state':userProfile.state,
'post_code':userProfile.post_code,
'country':userProfile.country,
})
return render_to_response('user_profile/update_profile.html', {'form':form}, RequestContext(request))
#login_required
def profile(request, profile_id):
if profile_id == "0":
if request.user.is_authenticated:
userProfile = UserProfile.objects.get(user=request.user)
else:
userProfile = UserProfile.objects.get(pk=profile_id)
return render_to_response('user_profile/profile.html', {'userProfile':userProfile}, RequestContext(request))
#login_required
def send_update_profile(request):
if request.method == 'POST':
form = UserProfileForm(request.POST)
if form.is_valid():
userProfile = UserProfile.objects.get(user=request.user)
organization = form.cleaned_data['organization']
userProfile.organization = organization
address_line_1 = form.cleaned_data['address_line_1']
userProfile.address_line_1 = address_line_1
address_line_2 = form.cleaned_data['address_line_2']
userProfile.address_line_2 = address_line_2
city = form.cleaned_data['city']
userProfile.city = city
state = form.cleaned_data['state']
userProfile.state = state
post_code = form.cleaned_data['post_code']
userProfile.post_code = post_code
country = form.cleaned_data['country']
userProfile.country = country
userProfile.save()
return redirect('/user/profile/' + str(userProfile.id))
else:
form = UserProfileForm()
return redirect('/user/send_update_profile/')
I can not work out why. One thing is that in admin.py, if i try to add 'id' as list display. An error occurs saying that there is no id in the user profile I am creating. Though, i thought an id field is automatically created when a model is created.
Any suggestions?
Django gives each model an automatic primary key field named id, unless you specify a primary key yourself. In your case, your model does not have an id field, because you have made user the primary key.
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name="custom_user_profile", primary_key=True)
You could change the code to use the userProfile.user_id. Another option is to use the pk shortcut to access the primary key.
return redirect('/user/profile/' + str(userProfile.pk))