Search code examples
pythondjangodjango-uploads

How do I save the foreign key?


I want to upload multiple images.

class IssuePanel(models.Model):
    issue = models.ForeignKey(ComicIssue, on_delete=models.CASCADE)
    panel = models.FileField(upload_to='comic_issues_files/panels/')
    date_uploaded = models.DateTimeField(auto_now_add=True)

After following the examples on django-multiupload's repository on github, I have this on forms.py

class PanelsForm(forms.ModelForm):

class Meta:

    model = ComicIssue
    fields = ('issue', 'issue_title', 'issue_cover', 'issue_description', 'issue_cover', 'issue_file')

panels = MultiFileField(min_num=1, max_num=20, max_file_size=2048*2048*5)

def save(self, commit=False):
    instance = super(PanelsForm, self).save()
    for each in self.cleaned_data['panels']:
        IssuePanel.objects.create(panel=each, issue=instance)
    return instance

views.py

class ComicIssueCreate(LoginRequiredMixin, CreateView):
    model = ComicIssue
    slug_field = 'comicseries_id'
    form_class = PanelsForm

    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.title = ComicSeries.objects.get(id=self.kwargs['pk'])
        obj.user = self.request.user
        obj.save()
        return redirect('comics:series_detail', pk=obj.title.id, slug=obj.title.slug)

urls.py

url(r'^comic/issue/(?P<pk>[0-9]+)/add/$', views.ComicIssueCreate.as_view(), name='comic-issue-add'),

However, I get this error IntegrityError at /comic/issue/21/add/ NOT NULL constraint failed: comics_comicissue.title_id

class ComicIssue(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, 
        null=True, blank=True, verbose_name='Uploaded by: '
    )
title = models.ForeignKey(ComicSeries, on_delete=models.CASCADE, verbose_name='Series Title')
issue = models.CharField(verbose_name='Issue Number', max_length=500)
issue_title = models.CharField(verbose_name='Issue Title', max_length=1000)
issue_cover = models.ImageField(verbose_name='Issue cover', upload_to='comic_issues', height_field=None, width_field=None, max_length=None)
issue_description = models.TextField(verbose_name='Description')
issue_file = models.FileField(verbose_name='Issue file', upload_to='comic_issues_files', max_length=100,
    help_text='File in pdf or as single image', null=True, blank=True
)
date_added = models.DateTimeField(auto_now_add=True, null=True)
is_favorite = models.BooleanField(default=False)
issue_slug = models.SlugField(default='')

class Meta:

    verbose_name = 'Comic Issue'
    verbose_name_plural = 'Comic Issues'

def __str__(self):
    return '{}: {} issue number - {}'.format(self.title.title, self.issue_title, self.issue)

def save(self, *args, **kwargs):
    self.issue_slug = slugify(self.issue_title)
    super(ComicIssue, self).save(*args, **kwargs)

def get_absolute_url(self):
    return reverse('comics:issue_detail', kwargs={'issue_slug':self.issue_slug,'pk': self.pk})

Could this function in the ComicIssue model be a problem since it is also highlighted on the error page:

def save(self, commit=False, *args, **kwargs):
        self.issue_slug = slugify(self.issue_title)
        super(ComicIssue, self).save(*args, **kwargs)

I am passing the title_id from the url. It is working on other models just not this one. How do I save the foreign key?


Solution

  • You're saving your form in the form's save method regardless of the commit value. The first line is instance = super(PanelsForm, self).save() which will try to save a ComicIssue instance even though in your view, you wrote obj = form.save(commit=False).

    You can do two things: pass the title and user to your form at init so the form can handle assigning those during save. Or change your form's save method to:

    def save(self, commit=False):
        instance = super(PanelsForm, self).save(commit=commit)
        if commit:
            for each in self.cleaned_data['panels']:
                IssuePanel.objects.create(panel=each, issue=instance)
        return instance
    

    And then your view needs to call the form's save() method twice (note that the form's instance is passed by reference, so changing it in the view also changes the form's instance):

    def form_valid(self, form):
        obj = form.save(commit=False)
        obj.title = ComicSeries.objects.get(id=self.kwargs['pk'])
        obj.user = self.request.user
        form.save()
        return redirect('comics:series_detail', pk=obj.title.id, slug=obj.title.slug)