django custom save insert to multiple table - django

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

Related

how to save a model object in another model save method

I have user model that has a one to one relation with two other models.
these are my models:
class User(AbstractUser):
id = models.AutoField(primary_key=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
isPreRegistered = models.BooleanField(default=False)
username = models.CharField(unique=True, max_length=13)
first_name = models.CharField(max_length=32, null=True, default=None)
last_name = models.CharField(max_length=64, null=True, default=None)
progress_level = models.CharField(max_length=25, null=True, choices=USER_PROGRESS_LEVELS)
class ScientificInfo(models.Model):
id = models.AutoField(primary_key=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
final_assessment = models.TextField(null=True, blank=True)
is_interviewed = models.BooleanField(default=False)
class PsychologicInfo(models.Model):
id = models.AutoField(primary_key=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
final_assessment = models.TextField(null=True, blank=True)
is_interviewed = models.BooleanField(default=False)
I want to update the user's progress_level if PsychologicInfo.is_interviewed and ScientificInfo.is_interviewed are both True. So I thought I should override the save method and added this to the user model:
def save(self, *args, **kwargs):
if self.scientificinfo.is_interviewed == True and self.psychologicinfo.is_interviewed == True:
self.progress_level = USER_PROGRESS_LEVELS[1][0]
return super(User, self).save(*args, **kwargs)
But I have to save the User object one more time to see some results. how can I update my progress level field when PsychologicInfo and ScientificInfo get saved?
I think you can use the Django signals a post_save can be what you need.
U can make a check if the instance PsychologicInfo or ScientificInfo
are updated or created then update the progress
voila an example of what you may want:
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import PsychologicInfo, ScientificInfo
# import your choices USER_PROGRESS_LEVELS
#receiver(post_save, sender=PsychologicInfo)
def pre_save_receiver(sender, instance, *args, **kwargs):
if instance:
if instance.psychologicinfo.is_interviewed:
instance.progress_level = USER_PROGRESS_LEVELS[1][0]
# Do other operation ...
You duplicate the this code by changing the sender and change the logique of your condition. it should work just fine

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 Models - Create ManyToManyField that connects to the members of a group

I have these models: Users, Grades, Groups, Projects, Tasks.
Every Grade, has Users (that can be student or teacher). The teacher can create groups of students. How do I create a ManyToManyField that connects to the specific users of that Grade?
Right Now when I try to make a group, it inherits from Grades and I get EK17A, TE17A (Note that i want to get the users in EK17A, or the users in EK17A)
This is so the teacher can put the students into project groups :D
from django.db import models
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 UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
age = models.IntegerField()
description = models.CharField(max_length=300)
is_student = models.BooleanField(default=False)
is_teacher = models.BooleanField(default=False)
class Meta:
verbose_name_plural = 'User Profiles'
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_data(sender, update_fields, created, instance, **kwargs):
if created:
user = instance
profile = UserProfile.objects.create(user=user, is_teacher=False, is_student=True, age=18, description='No Description')
class Grades(models.Model):
name = models.CharField(max_length=10)
members = models.ManyToManyField(UserProfile, blank=True)
class Meta:
verbose_name_plural = 'Grades'
def __str__(self):
return self.name
class TeacherProjectTask(models.Model):
title = models.CharField(max_length=150)
description = models.CharField(max_length=1000, blank=True)
state = models.CharField(max_length=20)
class Meta:
verbose_name_plural = 'Tasks'
def __str__(self):
return self.title
class TeacherProject(models.Model):
title = models.CharField(max_length=150)
description = models.CharField(max_length=1000)
tasks = models.ManyToManyField(TeacherProjectTask, blank=True)
grade = models.OneToOneField(Grades, blank=True, on_delete=models.CASCADE)
class Meta:
verbose_name_plural = 'Projects'
def __str__(self):
return self.title
class ProjectGroup(models.Model):
title = models.CharField(max_length=150)
description = models.CharField(max_length=1000)
members = models.ManyToManyField(Grades, blank=True)
class Meta:
verbose_name_plural = 'Group'
def __str__(self):
return self.title

Django - Create user profile on user creation

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)