Django Several ModelForm instances and forms on one template

516 Views Asked by At

I run into problems while setting up a user profile for our application. Imagine a linkedIn-like user profile with basic information, work- and education positions.

the model (simplified)

class UserProfile(models.Model):
    about_me = models.TextField(blank=True, null=True)
    formatted_name = models.CharField(max_length=120,  null=True, blank=True)
    username_slug = models.SlugField(null=True, blank=True, unique=True)
    location_name = models.CharField(max_length=120,  null=True, blank=True)
    ...

class JobPosition(models.Model):
    user_profile = models.ForeignKey(UserProfile)
    company_name = models.CharField(max_length=150, null=True, blank=True)
    title = models.CharField(max_length=150, null=True, blank=True)
    job_description = models.CharField(max_length=1000, null=True, blank=True)
    start_date = models.DateField(blank=True, null=True)
    end_date = models.DateField(blank=True, null=True)


class Education(models.Model):
    user_profile= models.ForeignKey(UserProfile)
    degree = models.CharField(max_length=60, null=True, blank=True)
    field_of_study = models.CharField(max_length=120, null=True, blank=True)
    school_name = models.CharField(max_length=150, null=True, blank=True)
    start_date = models.DateField(blank=True, null=True)
    end_date = models.DateField(blank=True, null=True)

Ok so obviously a user can have 0-n work positions ans 0-n education positions.

forms.py

class BasicForm(ModelForm):
    class Meta:
        model = UserProfile
        fields = ['id', 'about_me', 'linkedin_headline', 'location_name']
    # enter code here

class EducationForm(ModelForm):
    class Meta:
        model = Education
        fields = ['id', 'degree', 'field_of_study', 'school_name', 
                  'start_date','end_date']

class WorkForm(ModelForm):
    class Meta:
        model = JobPosition
        fields = ['id','company_name', 'title', 'job_description', 
                  'start_date','end_date']

views.py - I wanted to have everything in one function as I want to call no other URL. (see later in template). The user should be able to edit his positions on the site, click 'update' and get redirected back on the profile site. - I tried applying the apporach of distinguishing the post income via the name of the update button as seen here: How can I build multiple submit buttons django form? - it's currently not doing a lot of validation stuff

def detail(request, slug):
u = get_object_or_404(UserProfile, username_slug=slug)

#checking the request on Post 
if request.method == 'POST':
    # Battery of IF statements to determine which form has been used
    # if it's updating basic info
    if 'updateBasic' in request.POST:
        form = BasicForm(request.POST)
        if form.is_valid():
            form.save()

    # if its a Work Position
    if 'updateWork' in request.POST:
        form = WorkForm(request.POST)
        if form.is_valid():
            form.save()
# ... same for education...

# if there is no POST request, then the profile is just called to be displayed
else:
    basicForm = BasicForm()
    educForm = EducationForm()
    workForm = WorkForm()

#CSRF Token generation
c = {'user': u,
    'years' : range( datetime.now().year, 1980, -1),
    'months' : range( 1,13),
    'basicForm': basicForm,
    'educForm': educForm,
    'workForm': workForm}
#CSRF Token generation
c.update(csrf(request))

return render(request, 'userprofile/detail.html', c)

The template: Pretty straight forward. Looping over the work and education positions:

{% for work in user.positions %}
  <div id="work{{ forloop.counter }}" class="row">
    <div class="large-2 small-3 columns">
      <h6>  {{ work.end_date|date:'M Y'|default:"NOW"  }}<br>{{ work.start_date|date:'M Y'  }}</h6>

    </div>
    <div class="large-10 small-9 columns">
        <h6>{{ work.title }}
          <a class="show_hide editIcon" href="#" rel="#editWork{{ forloop.counter }} #work{{ forloop.counter }}" title="Edit"><i class="icon-edit pull-right editIcon icon-large"></i></a> 
        </h6>
        <h6 class="dblue ">{{ work.company_name }}</h6>
      {% if work.job_description %}
      <p> {{ work.job_description}} </p>
      {% endif %}
      {% if not forloop.last %}
      <hr>
      {% endif %}
    </div>
  </div>
  <div id="editWork{{ forloop.counter }}"  style="display: none;" class="row editForm">
    <div class="large-10 large-centered columns">
    <h5>Edit</h5>
      <form class="custom" action="#" method="post" name="WorkForm"> {% csrf_token %}
          <div class="row">
            <div class="large-6 columns">
              <label for="company">Company:</label>
              <input type="text"  name="company" id="company" placeholder="Example Ltd" required value="{{ work.company_name }}"/>
            </div>
            <div class="large-6 columns">
              <label for="title">Title:</label>
              <input type="text" name="title" id="title" placeholder="Example position" required value="{{ work.title }}"/>
            </div>
          </div>
...
<div class="row">
  <div class="large-12 columns">
     <label for="desc">Description</label>
     <textarea name="desc" id="desc" cols="40" rows="6" value="{{ edu.field_of_study_description }}"></textarea>
   </div>
</div>

<div class="row">
  <div class="large-2 small-3 columns">
<button class="tiny submit" type="submit" name="updateWork" title="Update this position" >update</button>
  </div>
  <div class="large-2 small-3 columns">
<button class="tiny submit" type="reset" title"Clear the form">Reset</button>
  </div>
  <div class="large-2 small-3 columns">
<a class="show_hide button tiny" href="#" rel="#editWork{{ forloop.counter }} #work{{ forloop.counter }}" title="Close the edit panel">Cancel</a> 
  </div>
  <div class="large-3 columns">
<a class="button tiny secondary" href="#"  title="Delete this position"> Delete</a>
  </div>
</div>
</form>
</div>
</div>
{% endfor %}

So what I do:

  • I loop over all work positions and generate a div for editing this exact position.
  • the div is shown when clicking on a 'edit Button' and javascript kicks in to make the div visible (slides over the non-edit div)
  • The user should then be able to edit this exact position. click update and I call the detail action to handle the update request. (not yet implemented)
  • I do the same for education
  • I know the forms are not integrated yet. As I really have no idea how to do this in a proper way...

Questions

  1. How can I properly display one of my modelforms for each position? (Formests are not really what I think I should use as they only allow a battery of forms rather then single instances)
  2. how can I fill it with the corresponding value? (can that even be done at runtime or do I habe to make an array of forms filled with the values already in the view and then pass this array to the template?)
  3. Am I in general on the right path? :)

Sorry for the lengthy post but I thought id rather give you as much info as possible so you can hopefully direct me the right way.

Already now lots of thanks in advance

Phil

0

There are 0 best solutions below