0

Imagine this concept, I have a Taxi that can be ordered by a group for a full day multiple visits, and I should assign a group leader for each booking. now I have a Booking (PNR) that holds Clients traveling Routes, and a Group Leader (Operator) assigned for that booking.

my view holds these:

  1. form for selecting the operator
  2. formset to add clients

in this view I'm trying to make it easier for the user by giving the ability to save each form separately by ajax or save all data of forms by a button at the bottom of the view.

I've been searching for a few days and I got the nearest approach on these two linkes 1 & 2 but still can't make my code run correctly and do what it's supposed to do. :( ANY SUPPORT WILL BE HIGHLY APPRECIATED!

My models.py:

class Operator (models.Model):
    name = models.CharField(max_length=50)
    # Other Fields

    def __str__(self):
        return self.code


class PNR (models.Model):
    date_created = models.DateTimeField(auto_now_add=True)
    # Other Fields

    def __str__(self):
        return self.pk


class Client (models.Model):
    related_pnr = models.ForeignKey(PNR, on_delete=models.CASCADE)
    name = models.CharField(max_length=200)
    # Other Fields

    def __str__(self):
        return self.related_pnr+" "+self.name

My forms.py:

class ChooseOperatorCode(forms.Form):
    operator = forms.ModelChoiceField(queryset=Operator.objects.all())

    def clean(self, *args, **kwargs):
        operator = self.cleaned_data.get('operator')

        return super(ChooseOperatorCode, self).clean(*args, **kwargs)


class NewClientForm(forms.Form):
    name = forms.CharField(widget=forms.TextInput(attrs={}), label=False, max_length=200)
    # Other Fields

    def clean(self, *args, **kwargs):
        name = self.cleaned_data.get('name')
        # Other Fields

        return super(NewClientForm, self).clean(*args, **kwargs)

My views.py:

@login_required
def create_pnr(request):

    pnr = PNR.objects.create(created_by_user=request.user)

    choose_operator_form = ChooseOperatorCode(request.POST or None)
    if choose_operator_form.is_valid():
        pnr.created_by_operator = choose_operator_form.cleaned_data.get('operator')
        pnr.save()
        choose_operator_form.save()

    clients_form = NewClientForm(request.POST or None)
    if clients_form.is_valid():
        client = Client()
        client.related_pnr = pnr.pk
        client.name = clients_form.cleaned_data.get('name')
        client.save()
        clients_form.save()
    context = {
        'pnr': pnr.pk,
        'choose_operator_form': choose_operator_form,
        'clients_form': clients_form,
    }
    return render(request, 'reservation_new.html', context)


@login_required
def edit_pnr(request, pnr_id):
    pnr = PNR.objects.get(id=pnr_id) 

    choose_operator_form = ChooseOperatorCode(request.POST or None)
    if choose_operator_form.is_valid():
        pnr.created_by_operator = choose_operator_form.cleaned_data.get('operator')
        pnr.save()

    clients_form = NewClientForm(request.POST or None)
    if clients_form.is_valid():
        client = Client()
        client.related_pnr = pnr.pk
        client.name = clients_form.cleaned_data.get('name')
        client.save()
        clients_form.save()
    context = {
        'pnr': pnr.pk,
        'choose_operator_form': choose_operator_form,
        'clients_form': clients_form,
    }
    return render(request, 'reservation_edit.html', context)

My url.py:

    path('operation/reservation/new/', views.create_pnr, name='new_pnr'),
    path('operation/reservation/existing/<int:pnr_id>/', views.edit_pnr, 
       name='existing_pnr'),

And Finally my template.html: (for both new and edit)

<form  method="POST" action="{% url 'existing_pnr' pnr_id=pnr %}" id="choose_operator_form">
     {% csrf_token %}
     {{choose_operator_form}}
</form>
<form  method="POST" action="{% url 'existing_pnr' pnr_id=pnr %}" id="clients_form">
     {% csrf_token %}
     {{clients_form}}
</form>
<script type="javascript">
    $(document).('submit', '#choose_operator_form', function(e){
        e.preventDefault();

        $.ajax({
            type:'POST',
            url:"{% url 'existing_pnr' %}",
            data: $('#choose_operator_form').serialize(),
            success: function (result) {
                // show success msg
            },
            error: function (data) {
                // show error msg
            }
        });
    });

    //same code for clients form
</script>
Poula Adel
  • 609
  • 1
  • 10
  • 33
  • What's the problem? What isn't working? Are you getting errors? Is some part of your code not doing what you expect it to do? – dirkgroten Apr 06 '20 at 16:46
  • two things I wanna know, can I actually apply this concept *give user ability to save each form separately with ajax and ability to submit all forms and formsets at once?* and how to apply this approach? the second thing that it giving me errors that when I hit save for ```choose_Operator_form``` says ```'WSGIRequest' object has no attribute 'pnr'``` – Poula Adel Apr 06 '20 at 16:56
  • yes, conceptually you can do that, as long as you remove the form after it was successfully submitted via ajax, so that it doesn't get submitted twice if the user then clicks "submit all". – dirkgroten Apr 06 '20 at 16:59
  • and second, why would you expect `request.pnr` to work? How can Django add a `pnr` to the `request` object? – dirkgroten Apr 06 '20 at 17:00
  • I've tried adding extra hidden input within the form but still same issue – Poula Adel Apr 06 '20 at 17:02
  • what issue do you mean by same issue? – dirkgroten Apr 06 '20 at 17:05
  • ```'WSGIRequest' object has no attribute 'pnr'``` – Poula Adel Apr 06 '20 at 17:06
  • 1
    But I told you, why would you expect `request.pnr` to work? Where is `pnr` coming from? Django has no idea of `pnr`. Normally, in an edit view, you have the id of the object to edit in the url, define a variable in the `path` (urls.py) and pass it to your view that would be `def edit_pnr(request, pnr_id): ...`. – dirkgroten Apr 06 '20 at 17:08
  • can you make answer to show how to pass the pnr to url in form action? because I tired to add variable to url.py like ```..//``` it raise error, and if I removed it it raises error too ```Reverse for 'existing_pnr' with keyword arguments '{'pnr_id': 94}' not found. 1 pattern(s) tried: ['operation\\/reservation\\/existing\\/$']``` – Poula Adel Apr 07 '20 at 09:53
  • Looks like you're using `pnr_id` instead of `pnr`. Make sure the url pattern, view and `reverse` lookup use the same variable name. – dirkgroten Apr 07 '20 at 09:55
  • yea I changed it, but it's not the error I'm aware of this point .. I've changed it everywhere – Poula Adel Apr 07 '20 at 09:58
  • 1
    `{% url 'existing_pnr' pnr_id=pnr %}` and `path('..//', ...)` – dirkgroten Apr 07 '20 at 10:01
  • Yea I tried this but not working :( It's driving me crazy for 3 days – Poula Adel Apr 07 '20 at 10:02
  • Show us your edited code, edit the question (add an edit at the bottom with all the new code and the new error you get, like this: "Edit: I now tried this: .... and it now gives me this error: ....). – dirkgroten Apr 07 '20 at 10:03
  • dude You are so supportive, you are appreciated! I just removed the ajax code every thing working now ! you can type your answer now and I'll Up vote it, you deserve it – Poula Adel Apr 07 '20 at 10:34

1 Answers1

1

The pnr doesn't get added to the request object, so request.pnr makes no sense. You need to pass the id of the PNR through the URL, that way it can be accessed in the view:

  • In urls.py, make sure that the url for your edit_pnr view gets the pnr_id: something like path('pnr/<int:pnr_id>/edit/', ..., name="existing_pnr").
  • In your template, construct the url for editing a pnr like this: {% url 'existing_pnr' pnr_id=pnr %}
  • In your view, receive the pnr_id: def edit_pnr(request, pnr_id): ...

Now you can fetch the PNR that's being edit like this: pnr = get_object_or_404(PNR, pk=pnr_id) which will correctly return a 404 Not Found if someone tries to access a non-existing PNR.

dirkgroten
  • 20,112
  • 2
  • 29
  • 42