Reputation: 603
I have a profile model which contains experience and education as foreign key fields. When I access profile template, it throws.
I tried post_save,
def create_education(sender, instance, created, **kwargs):
if created:
Profile.objects.create(education=instance)
post_save.connect(create_education, sender=CustomUser)
it throws this error,
How do I define a post_save signal so experience and education are created when I create a profile?
Note: I double checked the error is because foreign fields are empty i.e there is no error when I add experience and education fields in django admin.
models.py
class Work_Experience(models.Model):
job_title = models.CharField(max_length=100, null=True, blank=True)
company = models.CharField(max_length=100, null=True, blank=True)
description = models.CharField(max_length=300, null=True, blank=True)
exp_start_date = models.DateField(null=True, blank=True)
exp_end_date = models.DateField(null=True, blank=True)
class Education(models.Model):
degree = models.CharField(max_length=100, null=True, blank=True)
school = models.CharField(max_length=100, null=True, blank=True)
edu_start_date = models.DateField(null=True, blank=True)
edu_end_date = models.DateField(null=True, blank=True)
class Profile(models.Model):
experience = models.ForeignKey(Work_Experience, on_delete=models.SET_NULL, null=True, blank=True)
education = models.ForeignKey(Education, on_delete=models.SET_NULL, null=True, blank=True)
forms.py
class ProfileSettingsForm(forms.ModelForm):
job_title = forms.CharField(max_length=40, required=False)
company = forms.CharField(max_length=40, required=False)
description = forms.CharField(max_length=40, required=False)
exp_start_date = forms.DateField(required=False)
exp_end_date = forms.DateField(required=False)
degree = forms.CharField(max_length=40, required=False)
school = forms.CharField(max_length=40, required=False)
edu_start_date = forms.DateField(required=False, input_formats=settings.DATE_INPUT_FORMATS)
edu_end_date = forms.DateField(required=False, input_formats=settings.DATE_INPUT_FORMATS)
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance', None)
super(ProfileSettingsForm, self).__init__(*args, **kwargs)
self.fields['job_title'].initial = self.instance.experience.job_title
self.fields['company'].initial = self.instance.experience.company
self.fields['description'].initial = self.instance.experience.description
self.fields['exp_start_date'].initial = self.instance.experience.exp_start_date
self.fields['exp_end_date'].initial = self.instance.experience.exp_end_date
self.fields['degree'].initial = self.instance.education.degree
self.fields['school'].initial = self.instance.education.school
self.fields['edu_start_date'].initial = self.instance.education.edu_start_date
self.fields['edu_end_date'].initial = self.instance.education.edu_end_date
def save(self, commit=True):
model = super(ProfileSettingsForm, self).save(commit=False)
jt = self.cleaned_data['job_title']
co = self.cleaned_data['company']
desc = self.cleaned_data['description']
esd = self.cleaned_data['exp_start_date']
eed = self.cleaned_data['exp_end_date']
degree = self.cleaned_data['degree']
school = self.cleaned_data['school']
edusd = self.cleaned_data['edu_start_date']
edued = self.cleaned_data['edu_end_date']
if model.experience:
model.experience.job_title = jt
model.experience.company = co
model.experience.description = desc
model.experience.exp_start_date = esd
model.experience.exp_end_date = eed
model.experience.save()
else:
model.experience = Work_Experience.objects.create(job_title=jt,
company=co,
description=desc,
exp_start_date=esd,
exp_end_date=eed)
if model.education:
model.education.degree = degree
model.education.school = school
model.education.edu_start_date = edusd
model.education.edu_end_date = edued
model.education.save()
else:
model.education = Education.objects.create(degree=degree,
school=school,
edu_start_date=edusd,
edu_end_date=edued)
if commit:
model.save()
return model
Views.py
class ProfileSettingsView(UpdateView):
model = Profile
form_class = ProfileSettingsForm
pk_url_kwarg = 'pk'
context_object_name = 'object'
template_name = 'profile_settings.html'
def get_success_url(self):
return reverse_lazy('users:profile_settings', args = (self.object.id,))
UPDATE
If I remove the init() method in form, the error resolves. But I don't get the values from database in the form fields once I save it. How can I rewrite init() method?
def __init__(self, *args, **kwargs):
instance = kwargs.get('instance', None)
super(ProfileSettingsForm, self).__init__(*args, **kwargs)
self.fields['job_title'].initial = self.instance.experience.job_title
self.fields['company'].initial = self.instance.experience.company
self.fields['description'].initial = self.instance.experience.description
self.fields['exp_start_date'].initial = self.instance.experience.exp_start_date
self.fields['exp_end_date'].initial = self.instance.experience.exp_end_date
self.fields['degree'].initial = self.instance.education.degree
self.fields['school'].initial = self.instance.education.school
self.fields['edu_start_date'].initial = self.instance.education.edu_start_date
self.fields['edu_end_date'].initial = self.instance.education.edu_end_date
Upvotes: 1
Views: 1465
Reputation: 129
You cannot do that,i recommend you read django docs. just Do this: Update here
The code bellow work as expected..
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.base_user import AbstractBaseUser
from latiro_app.managers import UserManager
class User(AbstractBaseUser):
email = models.CharField(verbose_name='email or phone number ', max_length=50, unique=True )
first_name = models.CharField('first name', max_length=15,blank=True)
last_name = models.CharField('last name', max_length=15,blank=True)
country = CountryField(blank=True)
date_joined = models.DateTimeField('date joined', auto_now_add=True)
slug = models.SlugField('slug', max_length=50, unique=True, null=True)
is_active = models.BooleanField('active',default=False)
is_staff = models.BooleanField('staff', default=False)
email_confirmed = models.BooleanField(default=False)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
db_table = "users"
permissions = (
("edit_user", "Edit User"),
)
class WorkExperience(models.Model):
job_title = models.CharField(max_length=100, null=True, blank=True)
company = models.CharField(max_length=100, null=True, blank=True)
description = models.CharField(max_length=300, null=True, blank=True)
exp_start_date = models.DateField(null=True, blank=True)
exp_end_date = models.DateField(null=True, blank=True)
class Meta:
db_table = "experience"
def __str__(self):
return (self.job_title)
class Education(models.Model):
degree = models.CharField(max_length=100, null=True, blank=True)
school = models.CharField(max_length=100, null=True, blank=True)
edu_start_date = models.DateField(null=True, blank=True)
edu_end_date = models.DateField(null=True, blank=True)
class Meta:
db_table = "education"
def __str__(self):
return (self.degree)
class Profile (models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete= models.CASCADE,
verbose_name='list of users', null=True)
experience = models.ForeignKey(WorkExperience, on_delete=models.SET_NULL, null=True, blank=True)
education = models.ForeignKey(Education, on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self):
return (self.user)
class Meta:
db_table = "profile"
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_profile(sender, instance, created, **kwargs):
if created and not kwargs.get('raw', False):
profile = Profile(user=instance)
profile.save()
This should work. Tested on my database:
+----+--------------+---------------+---------+
| id | education_id | experience_id | user_id |
+----+--------------+---------------+---------+
| 1 | NULL | NULL | 2 |
+----+--------------+---------------+---------+
The null values on education_id and experience_id will be update by user_id instance when updating profile.
Now User can update his/her profile like this: note:i'm not using signal.
#Form.py
class EducationForm(forms.ModelForm):
degree = forms.CharField(max_length=40, required=False)
school = forms.CharField(max_length=40, required=False)
edu_start_date = forms.DateField(required=False,
input_formats=settings.DATE_INPUT_FORMATS)
edu_end_date = forms.DateField(required=False,
input_formats=settings.DATE_INPUT_FORMATS)
class Meta:
model= Education
fields =["degree","school", "edu_start_date","edu_start_date"]
#View.py
class EducationFormView(UpdateView):
model = Education
form_class = EducationForm
template_name = 'latiro_app/education_form.html'
def get_success_url(self):
return reverse_lazy('users:profile_settings',
args =(self.object.id,))
def get(self, form, ** kwargs):
profile_instance = get_object_or_404(self.model, user_id=self.kwargs['pk'])
#Pass user profile data to the form
context = {'form':self.form_class(instance=profile_instance)}
if self.request.is_ajax():
kwargs['ajax_form'] = render_to_string(self.template_name, context, request=self.request )
return JsonResponse(kwargs)
else:
return render(self.request, self.template_name, context)
Upvotes: 0
Reputation: 14360
In the line:
self.fields['job_title'].initial = self.instance.experience.job_title
you're dealing with a Profile
instance that does not have a related experience
.
If you want every time you create a Profile
it gets populated with am experience
and education
you should have a signal like:
def create_profile(sender, instance, created, **kwargs):
if created:
experience = Work_Experience.objects.create(profile=instance)
education = Education.objects.create(profile=instance)
post_save.connect(create_profile, sender=Profile)
post_save
signal is not triggered when calling the save()
of the form?model = super(ProfileSettingsForm, self).save(commit=False)
According the docs:
This save() method accepts an optional commit keyword argument, which accepts either True or False. If you call save() with commit=False, then it will return an object that hasn’t yet been saved to the database. In this case, it’s up to you to call save() on the resulting model instance. This is useful if you want to do custom processing on the object before saving it, or if you want to use one of the specialized model saving options. commit is True by default.
So by the time you do:
model.experience.job_title = jt
your post_save
signal hasn't been triggered and therefore model.experience
remains None
hence the error:
'NoneType' object has no attribute job_title.
Upvotes: 2