为外键字段创建post_save信号

时间:2019-02-08 19:31:26

标签: python django python-3.x django-models

我有一个概要模型,其中包含作为外键领域的经验和教育。 当我访问个人资料模板时,它会抛出。

enter image description here

我尝试了post_save,

def create_education(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(education=instance)

    post_save.connect(create_education, sender=CustomUser)

它会引发此错误,

enter image description here

如何定义保存后的信号,以便在创建个人资料时创建经验和教育?

注意:我再次检查了错误是因为外来字段为空,即在Django管理员中添加经验和教育领域时没有错误。

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,))

更新

如果我删除窗体中的init()方法,该错误将解决。但是一旦保存,就不会从表单字段中的数据库中获取值。 如何重写init()方法?

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 

2 个答案:

答案 0 :(得分:2)

为什么会出错?

在此行:

self.fields['job_title'].initial = self.instance.experience.job_title

您要处理的Profile实例没有相关的experience

如何定义保存后的信号,以便在创建个人资料时创建经验和教育?

如果您希望每次创建一个Profile时,它都填充有experienceeducation,那么您应该有一个类似的信号:

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时没有触发save()信号?

model = super(ProfileSettingsForm, self).save(commit=False)

根据文档:

  

此save()方法接受一个可选的commit关键字参数,该参数接受True或False。 如果您使用commit = False调用save(),它将返回一个尚未保存到数据库的对象。在这种情况下,您可以在生成的模型实例上调用save()。如果要在保存之前对对象进行自定义处理,或者要使用一种特殊的模型保存选项,这将很有用。默认情况下,commit为True。

所以到您这样做的时候:

model.experience.job_title = jt

您的post_save信号尚未触发,因此model.experience仍为None,因此出现错误:

  

“ NoneType”对象没有属性job_title。

答案 1 :(得分:0)

您不能这样做,我建议您阅读Django文档。只要这样做: 在此处更新

下面的代码按预期工作。

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()

这应该有效。在我的数据库上测试过:

+----+--------------+---------------+---------+
| id | education_id | experience_id | user_id |
+----+--------------+---------------+---------+
|  1 |         NULL |          NULL |       2 |
+----+--------------+---------------+---------+

更新个人资料时,user_id实例将更新education_id和experience_id上​​的空值。

现在,用户可以像这样更新其个人资料: 注意:我没有使用信号。

#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)