Django CRUD functionality with 2 models

Ok I´ve added it

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
            for i in images:
                ProjectImage.objects.create(project=project_instance, image=i)
            project.save()
    context = {'form':form}
    return render(request, 'projects/project_form.html', context)

You’re close -

  • You want the project.save() to be before the loop creating ProjectImage objects, not after. You need the reference to the saved Project object to use in the ProjectImage create call, and that object isn’t finalized until the save.

  • You want to pass your instance of Project as the project parameter within the create call. (e.g. ...create(project=project, ...) You don’t have a variable named project_instance at that point.

Ok if I got all things right, this should be the working version

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()
            for i in images:
                ProjectImage.objects.create(project=project, image=i)
    context = {'form':form}
    return render(request, 'projects/project_form.html', context)
1 Like

Thanks then I would adjust my update and delete view like this right?
views.py

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, instance=project)
        images = request.FILES.getlist('image')
        if form.is_valid():
            form.save()
            for i in images:
                ProjectImage.objects.update_or_create(project=project, image=i)
            return redirect('projects')

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

def deleteProject(request, pk):
    profile = request.user.profile
    project = profile.project_set.get(id=pk)

    if request.method == "POST":
        for i in images:
                ProjectImage.objects.update_or_create(project=project, image=i)
        project.delete()
        return redirect('account')
    context = {'object':project}
    return render(request, 'delete_template.html', context)

Update and delete operations are going to be different.

If you want to update or remove individual files associated with a project, you’ll probably want to use a formset (or more likely a ModelFormset) to assign an individual file to an individual form.

Keep in mind that changing or deleting a FileField object does not affect the file physically stored on the file system. This means your process to update the individual ProjectImage instances needs to identify when an individual instance has been changed, and remove the original file in addition to storing the new file.

Likewise, when a project is being deleted, you will need to go through all the ProjectImage instances and delete the files before deleting the Project instance.

Ok I think I never worked with formsets before but will try my best.
views.py:

from django.forms import modelformset_factory

def update_project(request, pk):
    profile = request.user.profile
    ProjectFormSet = modelformset_factory(Project, fields='title', 'describtion', 'price')
    form = ProjectFormSet(queryset=Project.object.get(id=pk))

    if request.method == 'POST':
        form = ProjectFormSet(request.POST, instance=project)
        images = request.FILES.getlist('image')
        if form.is_valid():
            form.save(comit=False)
            for i in images:
                ProjectImage.objects.update_or_create(project=project, image=i)
            return redirect('projects')

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

I found this: can_delete=True in the docs. Do I have to add this in my create_project_new. Or in other words. Can I create the ``delete_project```view without this parameter?

The only can_delete parameter I’m aware of is a formset parameter, which sets a flag indicating that each form in the formset could be flagged for deletion. It affects the UI, it has no effect on the underlying model.

You wouldn’t use it at all in create_project_new because as of the last version I have seen, you’re not using a formset there.

You don’t need it in your delete_project view, because if you’re deleting a project, you will be deleting all the images along with it.

You may want to use it in your update_project view, if you want to give the user the ability to delete individual images.

Ok thanks I just added it to my update_project for now. Could give me feedback to this one?

from django.forms import modelformset_factory

def update_project(request, pk):
    profile = request.user.profile
    ProjectFormSet = modelformset_factory(Project, can_delete=True, fields='title', 'describtion', 'price')
    form = ProjectFormSet(queryset=Project.object.get(id=pk))

    if request.method == 'POST':
        form = ProjectFormSet(request.POST, instance=project)
        images = request.FILES.getlist('image')
        if form.is_valid():
            form.save(comit=False)
            for i in images:
                ProjectImage.objects.update_or_create(project=project, image=i)
            return redirect('projects')

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

Some general thoughts, but with a caveat - I’ve never used a formset with an ImageField, so I don’t know if there are more issues than what I can see here.

First, it appears that this method is only allowing for the editing of the images. It doesn’t provide any facility for updating the Project object.

Also, read the complete section of the docs regarding can_delete, paying particular attention to the section that discusses the use of the commit=False parameter in the form.save() call.

Finally, keep in mind what I mentioned earlier regarding file fields. Deleting an object with a FileField does not delete the physical file in the file system. If you want the image files to be deleted along with the model instance, you need to do that yourself.

For now I would just do not add can_delete=True
To really delete the file I would add something like this:

 if instance.images:
        if os.path.isfile(instance. images.path):
            os.remove(instance.images.path)

Could you give me an overview on how I can really update the Project object

Your view would render a model form for Project with the fields you want to allow to have updated, and then you would include the formset as an additional field in the form.