2

I followed along the awesome tutorial How to Implement CRUD Using Ajax and Json by Vitor Freitas. For my project I have an object with a many to one relationship with the user that I want users to be able to add and update. I can add the object, but when I try to update it throws a ValueError: Cannot query "Case object": Must be "User" instance.

views.py

def case_update(request, pk):
    case = get_object_or_404(Case, pk=pk)
    if request.method == 'POST':
        form = CaseForm(request.POST, instance=case)
    else:
        form = CaseForm(instance=case)
    return save_case_form(request, form, 'cases/includes/partial_case_update.html')

It breaks when I try to save the edit, but I am struggling to see a way around this. To edit this case, I need the form to be an instance of this particular case as the user may have many cases. When I set the instance to user, nothing happens, when I remove the instance altogether it obviously just makes a copy of the case so I have two of the same cases.

I can post more code if necessary. Thank you

EDIT I refactored my code using only the inlineformset and it is working... kind of. I now can edit cases, but I still cannot edit a single case. I continue to receive a ValueError: Cannot query "Case object": Must be "User" instance when I try to do an instance of case = get_object_or_404(Case, pk=pk)with the inlineformset. When I change this to an instance of user it brings up all the cases of that particular user, but it saves correctly.

def save_case_form(request, case_formset, template_name):
    data = dict()

    if request.method == 'POST':
        if case_formset.is_valid():
            case_formset.save()
            data['form_is_valid'] = True
            cases = Case.objects.all()
            data['html_case_list'] = render_to_string('cases/includes/partial_case_list.html', {
                'cases': cases
            })
        else:
            data['form_is_valid'] = False
    context = {'case_formset' : case_formset}
    data['html_form'] = render_to_string(template_name, context, request=request)
    return JsonResponse(data)


def case_create(request):
    if request.method == 'POST':
        case_formset = CaseFormset(request.POST, instance = request.user)
    else:
        case_formset = CaseFormset()
    return save_case_form(request, case_formset, 'cases/includes/partial_case_create.html')


def case_update(request, pk):
    case = get_object_or_404(Case, pk=pk)
    if request.method == 'POST':
        case_formset = CaseFormset(request.POST, instance=request.user)
    else:
        case_formset = CaseFormset(instance=request.user)
    return save_case_form(request, case_formset, 'cases/includes/partial_case_update.html')

forms.py

class CaseForm(forms.ModelForm):
    class Meta:
        model = Case
        fields = ('title', 'publication_date', 'author', 'price', 'pages', 'case_type', )


CaseFormset = inlineformset_factory(User,Case, 
                                    fields = ('title', 'publication_date', 
                                              'author', 'price', 
                                              'pages', 'case_type', ), 
                                    can_delete = False,
                                    extra = 1)

EDIT

A non DRY implementation that works:

forms.py

class CaseForm(forms.ModelForm):
    class Meta:
        model = Case
        fields = ('title', 'publication_date', 'author', 'price', 'pages', 'case_type', )


CaseFormset = inlineformset_factory(User,Case, 
                                    fields = ('title', 'publication_date', 
                                              'author', 'price', 
                                              'pages', 'case_type', ), 
                                    can_delete = False,
                                    extra = 1)

CaseFormsetUpdate = inlineformset_factory(User,Case, 
                                    fields = ('title', 'publication_date', 
                                              'author', 'price', 
                                              'pages', 'case_type', ), 
                                    can_delete = False,
                                    extra = 0)

views.py

def save_case_form(request, case_formset, template_name):
    data = dict()

    if request.method == 'POST':
        if case_formset.is_valid():
            case_formset.save()
            data['form_is_valid'] = True
            cases = Case.objects.all()
            data['html_case_list'] = render_to_string('cases/includes/partial_case_list.html', {
                'cases': cases
            })
        else:
            data['form_is_valid'] = False
    context = {'case_formset' : case_formset}
    data['html_form'] = render_to_string(template_name, context, request=request)
    return JsonResponse(data)


def case_create(request):

    if request.method == 'POST':
        case_formset = CaseFormset(request.POST, instance = request.user)
    else:
        case_formset = CaseFormset()
    return save_case_form(request, case_formset, 'cases/includes/partial_case_create.html')


def case_update(request, pk):

    case = get_object_or_404(Case, pk=pk)
    if request.method == 'POST':
        case_formset = CaseFormsetUpdate(request.POST, instance=request.user)
    else:
        case_formset = CaseFormsetUpdate(instance=request.user, queryset = Case.objects.filter(pk=pk))
    return save_case_form(request, case_formset, 'cases/includes/partial_case_update.html')

I was unable to find a way to change the extra parameter on an inline formset when it is instantiated in views. So I just made a second one with extra set to 0 and added that to my update case view. I would still be interested in a better approach.

Jeff Tilton
  • 1,256
  • 1
  • 14
  • 28
  • Can you show us your form code? Also, where are you calling the actual `.save()`? – The_Cthulhu_Kid Apr 28 '17 at 04:54
  • @The_Cthulhu_Kid code as requested. There is a .js file as well where the form is serialized into json, but it breaks before that happens – Jeff Tilton Apr 28 '17 at 12:48
  • Cheers I'll have a look this morning. – The_Cthulhu_Kid Apr 29 '17 at 07:37
  • Why are you splitting the save View? – The_Cthulhu_Kid Apr 29 '17 at 07:40
  • @The_Cthulhu_Kid thanks for taking a look! I didn't even know this could be done until the tutorial I went through, but it is to cut down on code. There is a case create and case update view. They basically do the same thing so splitting up this section allows me to reuse this part of the code for both views – Jeff Tilton Apr 29 '17 at 14:45
  • If you want to reuse the code like that you are better off looking at class based views. You are filling the form then sending it and refilling it, so the code is still being rewritten really. I will write some code up as soon as I get a chance. Sorry for the delay! – The_Cthulhu_Kid Apr 30 '17 at 08:57
  • In your formset you are setting the instance to `request.user` is there a reason for that? – The_Cthulhu_Kid Apr 30 '17 at 11:17
  • @The_Cthulhu_Kid I would love to use class based views, but I am new to Django and am still confused / intimidated by them. I am setting the formset to instance user bc it was the only way I was able to save a case with the foreignkey relationship to user. I had tried `form.save(commit = false form.user = request.user form.save()` but that neither saved nor threw an error. – Jeff Tilton Apr 30 '17 at 15:13
  • Sorry, yesterday was a bank holiday. Once you are on invite me to a chat and we can go through some options. (I think you will get more out of it that way =) ) – The_Cthulhu_Kid May 02 '17 at 04:31
  • Also, you might want to try removing the `User` param from `inlineformset_factory` – The_Cthulhu_Kid May 02 '17 at 07:13
  • @The_Cthulhu_Kid I think I spied you on FB. I will try you tomorrow if you are around. I am on the west coast of the US so we are separated by a few time zones! Thanks – Jeff Tilton May 03 '17 at 03:06
  • If you found me on fb (/gileadslostson) just add me and I can help you from home rather than clutter up the comments =) – The_Cthulhu_Kid May 03 '17 at 04:46

1 Answers1

0

After a lot of trial and error I found a solution. I had been using inline formsets because I kept finding answers that pointed that direction, but I would rather do it manually if possible and I had been trying, but how I had been doing this did not work in this instance for some reason.

I had been trying something like:

case = form.save(commit = False)
case.user = request.user

or

form = (request.POST, instance = request.user)
form.save()

I came across this solution and it now works as intended without inline formsets.

form.instance.user = request.user
form.save()

This looks almost equivalent to what I had been doing, but it worked.

Community
  • 1
  • 1
Jeff Tilton
  • 1,256
  • 1
  • 14
  • 28