formaction vs get_absolute_url

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

formaction vs get_absolute_url

clavierplayer
I'm looking for a way to redirect to a different page depending upon which submit button the user clicks. I included a formaction tag in the template, but django still demands that get_absolute_url be defined. Using Django 2.0

Basically, I'm trying to write a small app that launches a test case, which would allow a non-programmer to evaluate a small section of code responsible for a background process. The app should allow the user to add as many test items to it as they wish, one at a time. When an item is added, they can choose to click and 'evaluate' button or an 'add more items' button, and each button should go to a different page--basically, 'add more items' should refresh the same page. Clicking on 'add more items' does indeed post the data to the database, but it ignores the formaction tag. The stack trace says that form_valid is calling get_absolute_url on the model (which I have not defined), but the target url has to do with what the user wants to do next with said model, not the model itself. I haven't figured out how to override the get_absolute_url call based on which html button was clicked. (Though it also said I can provide an url, which I did on the template...)

(The code I'm pasting below may be a little awkward because I've been finding workarounds for things by trial and error; so if anything particularly egregious catches someone's eye, comments to that end are welcome.)

Problem view:
class AddItemsToEvaluateCreateView(CreateView, FormMixin):
model = ItemsTestCollection
form_class = EnterItem
template_name = 'utils\evaluate.html'

def get_queryset(self):
return ItemsTestCollection.objects.filter(case_id=self.kwargs['pk'])

def get_object(self, queryset=None):
return EvaluateTestCase.objects.get(pk=self.kwargs['pk'])

def get_context_data(self, **kwargs):
context = super().get_context_data()
context['case_id'] = self.kwargs['pk']
context['items_in_order'] = ItemsTestCollection.objects.filter(case_id=self.kwargs['pk'])
return context

Problem models:
class ItemsTestCollection(models.Model):
"""An item to be tested in Box Opt."""
item = models.ForeignKey('item.Item', on_delete=models.CASCADE)
qty = models.IntegerField()
case = models.ForeignKey('EvaluateTestCase', on_delete=models.CASCADE)
box = models.ForeignKey('BoxResults', on_delete=models.CASCADE, null=True)


class EvaluateTestCase(models.Model):
"""Individual test cases."""

STATUS = (
('no', 'Something bad happened,'),
('hm', 'Needs improvement.'),
('ok', 'Seems all right.')
)

verdict = models.CharField(max_length=2, choices=STATUS, blank=False, null=True)
feedback = models.TextField(blank=True, null=True)

def get_absolute_url(self):
return reverse('utils:evaluate', kwargs={'pk': str(self.id)})

@staticmethod
def get_item_lots(case_number: int):
"""Get the first ItemLot for each item in the EvaluateTestCase."""
item_lots: List[ItemLot] = []
for item_collection in ItemsTestCollection.objects.filter(case_id=case_number):
item_lots.append(ItemLot.objects.filter(item=item_collection.item)[0])

return item_lots


Problem template:
<form method="post">
{% csrf_token %}
<table>
<tr>
<td><label for="item">Item #: </label></td>
<td><input type="text" id="item" name="item" autofocus/></td>
</tr>
<tr>
<td><label for="qty">Qty: </label></td>
<td><input type="text" id="qty" name="qty"/></td>
</tr>
<tr>
<td><label for="case">Case: </label></td>
<td><input type="text" id="case" name="case" value="{{ case_id }}" style="color:gray" readonly/></td>
</tr>
</table>
<input type="submit" formaction="{% url 'utils:evaluate' case_id %}" value="Add more items" />
<input type="submit" formaction="{% url 'utils:results' case_id %}" value="Optimize" />
</form>


Error:

ImproperlyConfigured at /utils/box-optimization/add-items/75

No URL to redirect to.  Either provide a url or define a get_absolute_url method on the Model.
Request Method:POST
Request URL:http://localhost:8000/utils/box-optimization/add-items/75
Django Version:2.0.5
Exception Type:ImproperlyConfigured
Exception Value:
No URL to redirect to.  Either provide a url or define a get_absolute_url method on the Model.
Exception Location:C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py in get_success_url, line 119
Python Executable:C:\miniconda3\envs\django\python.exe
Python Version:3.6.5
Python Path:
['C:\\WMS Repository\\Warehouse Management System',
 'C:\\WMS Repository\\Warehouse Management System',
 'C:\\miniconda3\\envs\\django\\python36.zip',
 'C:\\miniconda3\\envs\\django\\DLLs',
 'C:\\miniconda3\\envs\\django\\lib',
 'C:\\miniconda3\\envs\\django',
 'C:\\Users\\heast\\AppData\\Roaming\\Python\\Python36\\site-packages',
 'C:\\miniconda3\\envs\\django\\lib\\site-packages',
 'C:\\Program Files\\JetBrains\\PyCharm '
 '2017.3.3\\helpers\\pycharm_matplotlib_backend']
Server time:Thu, 12 Jul 2018 15:47:04 -0400

Environment:


Request Method: POST
Request URL: http://localhost:8000/utils/box-optimization/add-items/75

Django Version: 2.0.5
Python Version: 3.6.5
Installed Applications:
['item.apps.ItemConfig',
 'inventory.apps.InventoryConfig',
 'fulfillment.apps.FulfillmentConfig',
 'manifest.apps.ManifestConfig',
 'order.apps.OrderConfig',
 'utils.apps.UtilsConfig',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in get_success_url
  116.                 url = self.object.get_absolute_url()

During handling of the above exception ('ItemsTestCollection' object has no attribute 'get_absolute_url'), another exception occurred:

File "C:\miniconda3\envs\django\lib\site-packages\django\core\handlers\exception.py" in inner
  35.             response = get_response(request)

File "C:\miniconda3\envs\django\lib\site-packages\django\core\handlers\base.py" in _get_response
  128.                 response = self.process_exception_by_middleware(e, request)

File "C:\miniconda3\envs\django\lib\site-packages\django\core\handlers\base.py" in _get_response
  126.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\base.py" in view
  69.             return self.dispatch(request, *args, **kwargs)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\base.py" in dispatch
  89.         return handler(request, *args, **kwargs)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in post
  172.         return super().post(request, *args, **kwargs)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in post
  142.             return self.form_valid(form)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in form_valid
  126.         return super().form_valid(form)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in form_valid
  57.         return HttpResponseRedirect(self.get_success_url())

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in get_success_url
  119.                     "No URL to redirect to.  Either provide a url or define"

Exception Type: ImproperlyConfigured at /utils/box-optimization/add-items/75
Exception Value: No URL to redirect to.  Either provide a url or define a get_absolute_url method on the Model.


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/36757b9a-cbba-494e-9136-2208080432e4%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: formaction vs get_absolute_url

clavierplayer
Relevant urls:
app_name = "utils"
urlpatterns = [
path('', index, name="index"),
path('<str:message>', index, name="index"),
path('migrate/<str:migrate_step>', migrate, name="migrate"),

# -------------- Box Opt Evaluation urls --------------
path('box-optimization/instructions', instruct, name='instructions'),
path('box-optimization/add-items/<int:pk>', AddItemsToEvaluateCreateView.as_view(), name='evaluate'),
path('box-optimization/new-test-case', create_new_test_case, name='new-case'),
path('box-optimization/results/<int:pk>', EvaluateResultsUpdateView.as_view(), name='results')
]

On Thursday, July 12, 2018 at 4:04:25 PM UTC-4, [hidden email] wrote:
I'm looking for a way to redirect to a different page depending upon which submit button the user clicks. I included a formaction tag in the template, but django still demands that get_absolute_url be defined. Using Django 2.0

Basically, I'm trying to write a small app that launches a test case, which would allow a non-programmer to evaluate a small section of code responsible for a background process. The app should allow the user to add as many test items to it as they wish, one at a time. When an item is added, they can choose to click and 'evaluate' button or an 'add more items' button, and each button should go to a different page--basically, 'add more items' should refresh the same page. Clicking on 'add more items' does indeed post the data to the database, but it ignores the formaction tag. The stack trace says that form_valid is calling get_absolute_url on the model (which I have not defined), but the target url has to do with what the user wants to do next with said model, not the model itself. I haven't figured out how to override the get_absolute_url call based on which html button was clicked. (Though it also said I can provide an url, which I did on the template...)

(The code I'm pasting below may be a little awkward because I've been finding workarounds for things by trial and error; so if anything particularly egregious catches someone's eye, comments to that end are welcome.)

Problem view:
class AddItemsToEvaluateCreateView(CreateView, FormMixin):
model = ItemsTestCollection
form_class = EnterItem
template_name = 'utils\evaluate.html'

def get_queryset(self):
return ItemsTestCollection.objects.filter(case_id=self.kwargs['pk'])

def get_object(self, queryset=None):
return EvaluateTestCase.objects.get(pk=self.kwargs['pk'])

def get_context_data(self, **kwargs):
context = super().get_context_data()
context['case_id'] = self.kwargs['pk']
context['items_in_order'] = ItemsTestCollection.objects.filter(case_id=self.kwargs['pk'])
return context

Problem models:
class ItemsTestCollection(models.Model):
"""An item to be tested in Box Opt."""
item = models.ForeignKey('item.Item', on_delete=models.CASCADE)
qty = models.IntegerField()
case = models.ForeignKey('EvaluateTestCase', on_delete=models.CASCADE)
box = models.ForeignKey('BoxResults', on_delete=models.CASCADE, null=True)


class EvaluateTestCase(models.Model):
"""Individual test cases."""

STATUS = (
('no', 'Something bad happened,'),
('hm', 'Needs improvement.'),
('ok', 'Seems all right.')
)

verdict = models.CharField(max_length=2, choices=STATUS, blank=False, null=True)
feedback = models.TextField(blank=True, null=True)

def get_absolute_url(self):
return reverse('utils:evaluate', kwargs={'pk': str(self.id)})

@staticmethod
def get_item_lots(case_number: int):
"""Get the first ItemLot for each item in the EvaluateTestCase."""
item_lots: List[ItemLot] = []
for item_collection in ItemsTestCollection.objects.filter(case_id=case_number):
item_lots.append(ItemLot.objects.filter(item=item_collection.item)[0])

return item_lots


Problem template:
<form method="post">
{% csrf_token %}
<table>
<tr>
<td><label for="item">Item #: </label></td>
<td><input type="text" id="item" name="item" autofocus/></td>
</tr>
<tr>
<td><label for="qty">Qty: </label></td>
<td><input type="text" id="qty" name="qty"/></td>
</tr>
<tr>
<td><label for="case">Case: </label></td>
<td><input type="text" id="case" name="case" value="{{ case_id }}" style="color:gray" readonly/></td>
</tr>
</table>
<input type="submit" formaction="{% url 'utils:evaluate' case_id %}" value="Add more items" />
<input type="submit" formaction="{% url 'utils:results' case_id %}" value="Optimize" />
</form>


Error:

ImproperlyConfigured at /utils/box-optimization/add-items/75

No URL to redirect to.  Either provide a url or define a get_absolute_url method on the Model.
Request Method:POST
Request URL:<a href="http://localhost:8000/utils/box-optimization/add-items/75" target="_blank" rel="nofollow" onmousedown="this.href=&#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Flocalhost%3A8000%2Futils%2Fbox-optimization%2Fadd-items%2F75\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHPr2rxMuXRlMmMKGiSYzSYZMAAdQ&#39;;return true;" onclick="this.href=&#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Flocalhost%3A8000%2Futils%2Fbox-optimization%2Fadd-items%2F75\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHPr2rxMuXRlMmMKGiSYzSYZMAAdQ&#39;;return true;">http://localhost:8000/utils/box-optimization/add-items/75
Django Version:2.0.5
Exception Type:ImproperlyConfigured
Exception Value:
No URL to redirect to.  Either provide a url or define a get_absolute_url method on the Model.
Exception Location:C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py in get_success_url, line 119
Python Executable:C:\miniconda3\envs\django\python.exe
Python Version:3.6.5
Python Path:
['C:\\WMS Repository\\Warehouse Management System',
 'C:\\WMS Repository\\Warehouse Management System',
 'C:\\miniconda3\\envs\\django\\python36.zip',
 'C:\\miniconda3\\envs\\django\\DLLs',
 'C:\\miniconda3\\envs\\django\\lib',
 'C:\\miniconda3\\envs\\django',
 'C:\\Users\\heast\\AppData\\Roaming\\Python\\Python36\\site-packages',
 'C:\\miniconda3\\envs\\django\\lib\\site-packages',
 'C:\\Program Files\\JetBrains\\PyCharm '
 '2017.3.3\\helpers\\pycharm_matplotlib_backend']
Server time:Thu, 12 Jul 2018 15:47:04 -0400

Environment:


Request Method: POST
Request URL: <a href="http://localhost:8000/utils/box-optimization/add-items/75" target="_blank" rel="nofollow" onmousedown="this.href=&#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Flocalhost%3A8000%2Futils%2Fbox-optimization%2Fadd-items%2F75\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHPr2rxMuXRlMmMKGiSYzSYZMAAdQ&#39;;return true;" onclick="this.href=&#39;http://www.google.com/url?q\x3dhttp%3A%2F%2Flocalhost%3A8000%2Futils%2Fbox-optimization%2Fadd-items%2F75\x26sa\x3dD\x26sntz\x3d1\x26usg\x3dAFQjCNHPr2rxMuXRlMmMKGiSYzSYZMAAdQ&#39;;return true;">http://localhost:8000/utils/box-optimization/add-items/75

Django Version: 2.0.5
Python Version: 3.6.5
Installed Applications:
['item.apps.ItemConfig',
 'inventory.apps.InventoryConfig',
 'fulfillment.apps.FulfillmentConfig',
 'manifest.apps.ManifestConfig',
 'order.apps.OrderConfig',
 'utils.apps.UtilsConfig',
 'django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware']



Traceback:

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in get_success_url
  116.                 url = self.object.get_absolute_url()

During handling of the above exception ('ItemsTestCollection' object has no attribute 'get_absolute_url'), another exception occurred:

File "C:\miniconda3\envs\django\lib\site-packages\django\core\handlers\exception.py" in inner
  35.             response = get_response(request)

File "C:\miniconda3\envs\django\lib\site-packages\django\core\handlers\base.py" in _get_response
  128.                 response = self.process_exception_by_middleware(e, request)

File "C:\miniconda3\envs\django\lib\site-packages\django\core\handlers\base.py" in _get_response
  126.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\base.py" in view
  69.             return self.dispatch(request, *args, **kwargs)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\base.py" in dispatch
  89.         return handler(request, *args, **kwargs)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in post
  172.         return super().post(request, *args, **kwargs)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in post
  142.             return self.form_valid(form)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in form_valid
  126.         return super().form_valid(form)

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in form_valid
  57.         return HttpResponseRedirect(self.get_success_url())

File "C:\miniconda3\envs\django\lib\site-packages\django\views\generic\edit.py" in get_success_url
  119.                     "No URL to redirect to.  Either provide a url or define"

Exception Type: ImproperlyConfigured at /utils/box-optimization/add-items/75
Exception Value: No URL to redirect to.  Either provide a url or define a get_absolute_url method on the Model.


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/36153a66-837a-40f8-9fe3-6016c7fc1a5d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: formaction vs get_absolute_url

Daniel Roseman-2
In reply to this post by clavierplayer
On Thursday, 12 July 2018 21:04:25 UTC+1, [hidden email] wrote:
I'm looking for a way to redirect to a different page depending upon which submit button the user clicks. I included a formaction tag in the template, but django still demands that get_absolute_url be defined. Using Django 2.0

Basically, I'm trying to write a small app that launches a test case, which would allow a non-programmer to evaluate a small section of code responsible for a background process. The app should allow the user to add as many test items to it as they wish, one at a time. When an item is added, they can choose to click and 'evaluate' button or an 'add more items' button, and each button should go to a different page--basically, 'add more items' should refresh the same page. Clicking on 'add more items' does indeed post the data to the database, but it ignores the formaction tag. The stack trace says that form_valid is calling get_absolute_url on the model (which I have not defined), but the target url has to do with what the user wants to do next with said model, not the model itself. I haven't figured out how to override the get_absolute_url call based on which html button was clicked. (Though it also said I can provide an url, which I did on the template...)

(The code I'm pasting below may be a little awkward because I've been finding workarounds for things by trial and error; so if anything particularly egregious catches someone's eye, comments to that end are welcome.)


You've misdiagnosed the problem. formaction is working fine; the data is being posted to your add-items view. The issue is what happens next. Any successful POST should be followed by a redirect, and CreateView by default uses the value of get_absolute_url to determine where to direct to.

Actually, formaction isn't what you want here at all. You've almost mentioned the solution by talking about overriding get_absolute_url based on the HTML button; that's not quite it, what you actually need to do is to override the method responsible for calling it, which is the view's get_success_url.

class AddItemsToEvaluateCreateView(CreateView):
    ...
    def get_success_url(self):
        if 'add_more' in self.request.POST:
            return reverse('evaluate', kwargs={'pk': self.kwargs['pk']}
        elif 'optimize' in self.request.POST:
            return reverse('results', kwargs={'pk': self.kwargs['pk']}
        return '/'


Also you'll need to change your input elements; they don't need `formaction`, but they do need a `name` so that they are sent in the POST data:

    <input type="submit" name="add_more" value="Add more items" />
    <input type="submit" name="optimize" value="Optimize" />

--
DR.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/6e1cd862-f528-4b0b-bd91-53530c904d31%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Reply | Threaded
Open this post in threaded view
|

Re: formaction vs get_absolute_url

clavierplayer
Thank you for the clear explanation! It works beautifully now.

On Friday, July 13, 2018 at 9:17:37 AM UTC-4, Daniel Roseman wrote:
On Thursday, 12 July 2018 21:04:25 UTC+1, [hidden email] wrote:
I'm looking for a way to redirect to a different page depending upon which submit button the user clicks. I included a formaction tag in the template, but django still demands that get_absolute_url be defined. Using Django 2.0

Basically, I'm trying to write a small app that launches a test case, which would allow a non-programmer to evaluate a small section of code responsible for a background process. The app should allow the user to add as many test items to it as they wish, one at a time. When an item is added, they can choose to click and 'evaluate' button or an 'add more items' button, and each button should go to a different page--basically, 'add more items' should refresh the same page. Clicking on 'add more items' does indeed post the data to the database, but it ignores the formaction tag. The stack trace says that form_valid is calling get_absolute_url on the model (which I have not defined), but the target url has to do with what the user wants to do next with said model, not the model itself. I haven't figured out how to override the get_absolute_url call based on which html button was clicked. (Though it also said I can provide an url, which I did on the template...)

(The code I'm pasting below may be a little awkward because I've been finding workarounds for things by trial and error; so if anything particularly egregious catches someone's eye, comments to that end are welcome.)


You've misdiagnosed the problem. formaction is working fine; the data is being posted to your add-items view. The issue is what happens next. Any successful POST should be followed by a redirect, and CreateView by default uses the value of get_absolute_url to determine where to direct to.

Actually, formaction isn't what you want here at all. You've almost mentioned the solution by talking about overriding get_absolute_url based on the HTML button; that's not quite it, what you actually need to do is to override the method responsible for calling it, which is the view's get_success_url.

class AddItemsToEvaluateCreateView(CreateView):
    ...
    def get_success_url(self):
        if 'add_more' in self.request.POST:
            return reverse('evaluate', kwargs={'pk': self.kwargs['pk']}
        elif 'optimize' in self.request.POST:
            return reverse('results', kwargs={'pk': self.kwargs['pk']}
        return '/'


Also you'll need to change your input elements; they don't need `formaction`, but they do need a `name` so that they are sent in the POST data:

    <input type="submit" name="add_more" value="Add more items" />
    <input type="submit" name="optimize" value="Optimize" />

--
DR.

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to [hidden email].
To post to this group, send email to [hidden email].
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/a47aef1d-0dcd-4d35-9f0f-aaa0ba43c8ee%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.