Django CRUD functionality with 2 models

I was building the CRUD-functionality with create_project_old, update_project, delete_project. Then I slightly switched my models.py and create_project_new view but I unfortunately do not really know how I have to change my update_project and delete_project views now. In addition I have to add the “user-related-part” in create_project_new. I have also posted my new models.py and forms.py. Thanks!

views.py

def create_project_new(request):
    form = ProjectForm()
    form2 = ProjectImageForm()

    if request.method == 'POST':
        form = ProjectForm(request.POST)
        form2 = ProjectImageForm(request.POST, request.FILES)
        images = request.FILES.getlist('image')
        if form.is_valid() and form2.is_valid():
            title = form.cleaned_data['title']
            describ = form.cleaned_data['describtion']
            price = form.cleaned_data['price']
            project_instance = Project.objects.create(
                title=title, describtion=describ, price=price)
            print(project_instance)

            for i in images:
                ProjectImage.objects.create(project=project_instance, image=i)
            return redirect('projects')

    context = {'form': form, 'form2': form2}
    return render(request, 'projects/project_form.html', context)


def create_project_old(request):
    profile = request.user.profile
    form = ProjectForm()

    if request.method == 'POST':
        form = ProjectForm(request.POST, request.FILES)
        if form.is_valid():
            project = form.save(commit=False)
            project.owner = profile
            project.save()
            return redirect('account')

    context = {'form':form}
    return render(request, 'projects/project_form.html', context)



@login_required(login_url='login')
def update_project(request, pk):
    profile = request.user.profile
    project = profile.project_set.get(id=pk)
    form = ProjectForm(instance=project)

    if request.method == 'POST':
        form = ProjectForm(request.POST, request.FILES, instance=project)
        if form.is_valid():
            form.save()
            return redirect('projects')

    context = {'form':form}
    return render(request, 'projects/project_form.html', context)


@login_required(login_url='login')
def deleteProject(request, pk):
    profile = request.user.profile
    project = profile.project_set.get(id=pk)
    if request.method == "POST":
        project.delete()
        return redirect('account')
    context = {'object':project}
    return render(request, 'delete_template.html', context)

models.py

class Project(models.Model):
    owner = models.ForeignKey(Profile, null=True, blank=True, on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    describtion = models.TextField(null=True, blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2, default=10)
    id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)


    def __str__(self):
        return self.title

class ProjectImage(models.Model):
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    image = models.FileField(upload_to="products/")
    
    def __str__(self):
        return self.image

forms.py

class ProjectForm(forms.ModelForm):
    class Meta:
        model = Project
        fields = ['title', 'describtion', 'price']

class ProjectImageForm(forms.ModelForm):
    class Meta:
        model = ProjectImage
        fields = ['image']
        widgets = {
            'image': ClearableFileInput(attrs={'multiple': True}),
        }

What is the new behavior that you are looking to implement in your update_project and delete_project views?

Also, you ask:

Why do you think that would be different from how you’ve done it in your create_project_old? Has the requirement changed for it?

In my old models.py I had just one model. But I´ve made it possible to let the user upload multiple images. Therefore I´ve created two models and changed create_project_old to create_project_new. That´s the whole new behavior. I just do not know what I have to change in those three views.
No further requirements changed from the previous ones.

Ok, let’s approach these one-at-a-time to try and avoid confusion among them. Since you’re already showing a create_project_new method, that might be the best one to start with.

What is it not doing that you want it to do?

The creation of new a post seems to work but in create_project_old I have the line of profile = request.user.profile and project.owner = profile. In create_project_new they do not appear so I was wondering if I have to add them?

If you want to assign the owner attribute of the project object to request.user.profile, then yes.

So I want that a project is always related to a specific owner. Would the following code be right then

def create_project_new(request):
    profile = request.user.profile
    form = ProjectForm()
    form2 = ProjectImageForm()

    if request.method == 'POST':
        form = ProjectForm(request.POST)
        form2 = ProjectImageForm(request.POST, request.FILES)
        images = request.FILES.getlist('image')
        if form.is_valid() and form2.is_valid():
            project.owner = profile
            title = form.cleaned_data['title']
            describ = form.cleaned_data['describtion']
            price = form.cleaned_data['price']
            project_instance = Project.objects.create(
                title=title, describtion=describ, price=price)
            print(project_instance)

            for i in images:
                ProjectImage.objects.create(project=project_instance, image=i)
            return redirect('projects')

    context = {'form': form, 'form2': form2}
    return render(request, 'projects/project_form.html', context)

Since ProjectForm is a ModelForm for Project, it’s a lot easier than that.

You have from your create_project_old:

The part marked out in “#” doesn’t need to be changed just because you’re adding a second form on your page. You don’t need to manually build the Project object.

Sorry but I do not really get the content here. My new view for creating a project is create_project_new. But you send me the old view create_project_old. My question was if I have to add the lines profile = request.user.profile and project.owner = profile which I have in my old view but not in my new view. Nevertheless create_project_new seems to work. Thanks.

I pointed out that what you have in create_project_old is what you should be doing in create_project_new, not all that extra code that you added to create_project_new for creating the Project object.

So in my views I am defining just one form? And in addition this part:

                ProjectImage.objects.create(project=project_instance, image=i)
            return redirect('projects')```
isn´t necassary?

Within your new view, you have:

This specific block of code in new:

is manually doing what this block does in the old:

I am only referring to how you’re handling the Project object - none of what I’m addressing here is any reference to how you’re handling ProjectImage.
(That’s a follow-on topic that I’ll address after we’re clear here - I’m trying to avoid confusion between the two.)

Okay now I am getting it. Thanks. So what you have posted is all forProjectForm in the create_project_new right? Like this:

def create_project_new(request):
    form = ProjectForm()
    profile = request.user.profile

        if form.is_valid():
            project.owner = profile
            title = form.cleaned_data['title']
            describ = form.cleaned_data['describtion']
            price = form.cleaned_data['price']
            project_instance = Project.objects.create(
                title=title, describtion=describ, price=price)
            print(project_instance)

    context = {'form': form}
    return render(request, 'projects/project_form.html', context)

I think there’s still some miscommunication here.

What you have here:

This is what you don’t want to have to create your Project model.

What you had originally in your “old” version is what you want to use.

This code replaces what you are showing for your “new” method.

And again for clarity, this is only addressing the part of this method for creating the Project object. You still need the appropriate code to create the ProjectImage instances in the same view.

Ok I think that should do it for the Project object. I have defined the ProjectImage object as well but I did not do anything after validating it for now.

def create_project_new(request):
    form = ProjectForm()
    form2 = ProjectImageForm()
    profile = request.user.profile

    if request.method == 'POST':
        form = ProjectForm(request.POST)
        form2 = ProjectImageForm(request.POST, request.FILES)
        images = request.FILES.getlist('image')

        if form.is_valid() and form2.is_valid():
            project = form.save(commit=False)
            project.owner = profile
            project.save()
    context = {'form':form, 'form2':form2}
    return render(request, 'projects/project_form.html', context)

Good. Now for the ProjectImage objects, you’ve defined a ModelForm for them. However, you’re not actually using that form as a ModelForm.

I actually wouldn’t make a separate form for this - and certainly not a ModelForm.

I’d add a project_image field to the Project form. Not as a model field, but as an additional form field.

That reduces the need from two forms to one, generally simplifying the code.

You can still create your ProjectImage objects the same way you have shown in your method.

Do you mean something like that? In addition I keep two models right?
forms.py:

class ProjectForm(ModelForm):
    image = forms.ImageField(widget=ClearableFileInput(attrs={'multiple':True}))
    class Meta:
        model = Project
        fields = ['title', 'describtion', 'price']

models.py

class Project(models.Model):
    owner = models.ForeignKey(Profile, null=True, blank=True, on_delete=models.CASCADE)
    title = models.CharField(max_length=200)
    describtion = models.TextField(null=True, blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2, default=10)
    id = models.UUIDField(default=uuid.uuid4, unique=True, primary_key=True, editable=False)


    def __str__(self):
        return self.title

class ProjectImage(models.Model):
    project = models.ForeignKey(Project, on_delete=models.CASCADE)
    image = models.FileField(upload_to="products/")
    
    def __str__(self):
        return self.image

This is correct - in both cases. (Form and Models)

You can still create your ProjectImage objects the same way you have shown in your method.
Do you mean my first create_project_new or create_project_old?

So then my view should look like this:

def create_project_new(request):
    form = ProjectForm()
    profile = request.user.profile

    if request.method == 'POST':
        form = ProjectForm(request.POST)
        images = request.FILES.getlist('image')

        if form.is_valid():
            project = form.save(commit=False)
            project.owner = profile
            project.save()
    context = {'form':form}
    return render(request, 'projects/project_form.html', context)

In create_project_new. I do not see any code in create_project_old that would create any ProjectImage objects. (The only complete instance I see of the old method is in the original post)