Ad

How Do Create A Custom Save Method For An Existing M2m Field. (Django)

  • I have a profile model which contains education and created a many to many relation i.e many profiles can have many educations.
  • I have a template called Profile settings which contains multiple forms like Education CreateView, Education UpdateView and Education ListView. I'm using bootstrap modals to display these forms. Hence the they share same template.
  • Problem is whenever I save education form, it saves form in database and it shows in the education multiselect field of that profile in django admin but unselected, it does not create a m2m link between profile and education.
  • I overrided the save() method to create the link as suggested by the community but I'm not able to get the current profile. Getting this error after saving. ('EducationForm' object has no attribute 'request')

Models

class Profile(models.Model):
    phone         = models.CharField(max_length=11, null=True, blank=True)
    education     = models.ManyToManyField(Education, null=True, blank=True, related_name="education")
    full_name     = models.CharField(max_length=30, 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)

View

class EducationView(CreateView):
    model = Education
    form_class = EducationForm
    pk_url_kwarg = 'pk'
    template_name = "profile_settings.html"

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

    def get_object(self):
        pk = self.kwargs.get('pk')
        return get_object_or_404(Profile, id=pk)

    def get_context_data(self, **kwargs):
        context = super(ProfileSettingsView, self).get_context_data(**kwargs)
        context['prof'] = self.get_object()
        return context

Form

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,
                                     widget=forms.DateInput(attrs={'readonly': 'readonly'}))
    edu_end_date = forms.DateField(required=False,
                                   input_formats=settings.DATE_INPUT_FORMATS,
                                   widget=forms.DateInput(attrs={'readonly': 'readonly'}))

    def save(self, commit=True):
        edu = super(EducationForm, self).save(commit=False)
        edu.save()
        profile = Profile.objects.get(self.request.user.profile.id)
        profile.education.add(education=edu)

        return edu


    class Meta:
        model = Education

        fields = ['degree','school','edu_start_date','edu_end_date']

class ProfileSettingsForm(forms.ModelForm):
    class Meta:
        model = Profile

        fields = ['phone','full_name','education']

        widgets = {

            'full_name': forms.TextInput({'class': 'form-control'}),
            'phone': forms.TextInput({'class': 'form-control'}),
        }

urls

urlpatterns = [
path('<int:pk>/education/update/', views.EducationUpdate.as_view(), name='education_update'),
path('<int:pk>/profile/settings', views.ProfileSettingsView.as_view(), name='profile_settings'),
    ]

Template (Profile_settings.html)

<form action="#" style="padding-left: 15px; padding-right:15px;" method="POST">
        {{ form.errors }}
        {% csrf_token %}
    <div class="row">
        <div id="education_update" class="modal fade" role="dialog">
          <div class="modal-dialog">

            <div class="modal-content">
              <div class="modal-header">
                  <h4 class="modal-title">New Education</h4>
                <button type="button" class="close" data-dismiss="modal">&times;</button>
              </div>
              <div class="modal-body">
                <div class="row">
                <div class="col-lg-12">
                    <div class="form-group">
                        <div class="field">
                            <label>Degree title<span class="red-txt">*</span></label>
                            <!-- <input class="form-control" type="text" name="degree" value="" maxlength="60" size="50"> -->
                            {{ edu_object.degree }}
                        </div>
                    </div>
                </div>
                <div class="col-lg-12">
                    <div class="form-group">
                        <div class="field">
                            <label>School<span class="red-txt">*</span></label>
                            <!-- <input class="form-control" type="text" name="school" value="" maxlength="60" size="50"> -->
                            {{ edu_object.school }}
                        </div>
                    </div>
                </div>

                <div class="col-lg-6">
                    <div class="form-group">
                        <div class="field">
                            <label>Start date</label>
                            <div id="datepicker1" class="datepicker input-group date" data-date-format="yyyy-mm-dd">
                                {{ edu_object.edu_start_date }}
                                <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="col-lg-6">
                    <div class="form-group">
                        <div class="field">
                            <label>End date</label>
                            <div id="datepicker2" class="datepicker input-group date" data-date-format="yyyy-mm-dd">
                                {{ edu_object.edu_end_date }}
                                <span class="input-group-addon"><i class="fa fa-calendar"></i></span>
                            </div>
                        </div>
                    </div>
                </div>

                <div class="col-lg-12">
                    <br>

                    <div style="text-align:center;" class="login login-button">
                        <input type="submit" class="btn btn-outline-primary btn-lg" style="cursor: pointer;" value="Save">
                    </div>
                </div>
            </div>
              </div>
            </div>

          </div>
        </div>
    </div>
</form>


{% if prof %}
<table class="table">
    <tbody>
        {% for edu in prof.education.all %}
            <tr class="divbutton" style="height: 90px;">
                <td>
                    <div class="row">
                        <div style="padding-left: 40px; font-size: 20px;">{{ edu.degree }}</div>
                        <div style="padding-left: 40px; font-size: 20px;">{{ edu.school }}</div>
                    </div>
                </td>
                <td class="align-middle">
                    <div class="row">
                        <div id="button_under" style="margin-right: 20px;" class="login login-button">
                            <a target="_blank" rel="nofollow noreferrer" href="#" class="btn btn-info" data-toggle="modal" data-target="#education_update" data-backdrop="false"><i class="fa fa-edit"></i> Edit</a>
                        </div>
                        <div id="button_under" class="login login-button">
                            <a target="_blank" rel="nofollow noreferrer" href="" class="btn btn-danger"><i class="fa fa-trash"></i> Delete</a>
                        </div>
                    </div>
                </td>
            </tr>
        {% endfor %}
        {% endif %}
    </tbody>
</table>
Ad

Answer

You'd need to pass the current user or request to the Form instance, so you could override get_form_kwargs in EducationView:

def get_form_kwargs(self):
    kwargs = super(EducationView, self).get_form_kwargs()
    kwargs['request'] = self.request
    return kwargs

and then initialize the EducationForm like this:

def __init__(self, request,  *args, **kwargs):
    super(EducationForm, self).__init__(*args, **kwargs)
    self.request = request

As an aside, shouldn't the related_name field in Profile be profiles?

Ad
source: stackoverflow.com
Ad