Valeriy
Valeriy

Reputation: 103

How to call a view with many parameters

I am trying to create a very simple custom view for my application. Lets imagine I have a simply model:

class Person(models.Model):
    Name = models.CharField(max_length = 255)
    pass

class Company(models.Model):
    Title = models.CharField(max_length = 255)
    pass

I would like to display a list of objects. On one url - list of person, on another - list of companies. So I create simply views:

def PersonListView (request):
    _persons = Person.objects.all()
    context = {
        "object_list": _persons
    }
    return render (request, "PersonListView.html", context)

def CompanyListView (request):
    _companies = Person.objects.all()
    context = {
        "object_list": _companies
    }
    return render (request, "CompanyListView.html", context)

Then add path to urls - /Person for list of persons and /Company for list of companies.

urlpatterns = [
    path('/Person', PersonListView), 
    path('/Company', CompanyListView)
]

All working well. But I already have a questions:

  1. Why my view function have a request variable, but I can call this function from urls without defining this variable?

  2. Why I can't use this syntax path('/Person', PersonListView())? What is wrong with this brackets?

And another part of my question. But then I decided to make some refactoring - I intended to use one view both for person and companies, so I have to pass a variable context to view function. And I basically know how to do it:

def ObjectList (request, _context):
    _objects = _context.objects.all()
    data = {
        "object_list": _objects
    }
    return render (request, "ListView.html", data)

Here _context - concrete class (Person or Company).

My problem is in urls.py I have to call ObjectList and pass 2 variables:

_context - there is no problem here, I know how to do it

and request.

Here I faced a wall, because I don't understand how can I pass it to my function. If I just let it empty, I have an error "ObjectList() missing 1 required positional argument: 'request'"

  1. So how can I fix it? SHould I somehow pass it from my urls? Is there a way to use a view function with multiple arguments?

Here is the traceback:

Internal Server Error: /Entities/Person/
Traceback (most recent call last):
  File "D:\Work\Python\virtualenvs\TestProject\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "D:\Work\Python\virtualenvs\TestProject\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "D:\Work\Python\virtualenvs\TestProject\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
TypeError: ObjectList() got multiple values for argument '_context'
[25/Jul/2019 00:25:37] "GET /Entities/Person/ HTTP/1.1" 500 62910

Upvotes: 0

Views: 329

Answers (1)

Daniel Roseman
Daniel Roseman

Reputation: 599630

Your second question answers your first. You don't call the view from the URL; Django does that internally when it encounters a matching request. If you try to call it from there, then you get the error you see.

I don't understand though why you think you need to pass request from the URL in the third question. You don't, that's the whole point of your questions 1 and 2.

Your URL pattern should either capture the arguments for the view, or pass them as the third parameter to path. So, in your case, you could use a single pattern which captures the variable:

path('/<str:object_type>/', object_list, name='object_list')

and then:

def object_list(request, object_type):
    types = {'Person': models.Person, 'Company': models.Company}
    if object_type not in types:
        raise Http404
    objects = types[object_type].objects.all()

Alternatively, use two separate URL patterns and pass the type as the third parameter explicitly:

urlpatterns = [
    path('/Person', object_list_view, {'object_type': Person}, name='person_list'), 
    path('/Company', object_list_view, {'object_type': Company}, name='company_list'),
]

and your view can be:

def object_list(request, object_type):
    objects = object_type.objects.all()

Finally, though, for this very simple use case you should consider using a generic list view; then you wouldn't need to define any views at all:

from django.views.generic import ListView
urlpatterns = [
    path('/Person', ListView.as_view(model=Person)), 
    path('/Company', ListView.as_view(model=Company))
]

Upvotes: 1

Related Questions