In Django, how to get the return of two functions in the same html page? Error: TypeError: kwargs argument must be a dict, but got function

huangapple go评论58阅读模式
英文:

In Django, how to get the return of two functions in the same html page? Error: TypeError: kwargs argument must be a dict, but got function

问题

在Django中,我有两个包含在views.py中的函数。一个称为def city_detail,另一个称为def DemoView。它们都返回return render(request, 'app1/home.html', context)

url.pyurlpatterns中,我想同时使用这两个函数,但似乎一次只能使用一个。如果我只使用其中一个,那么它可以正常工作(只有其中一个起作用)。如果同时使用这两个函数,我会得到错误:

path('home/', views.city_detail, views.DemoView, name='home'),
path('home/', views.DemoView, name='home'),

我得到以下错误:

raise TypeError(
TypeError: kwargs argument must be a dict, but got function.

我如何在主页中同时使用这两个函数?

更新

我通过编写整个代码来更新问题。

我相信不需要代码,因为我认为解决方案更简单。更新以改进问题。我添加了我在项目中使用的代码。

home.html是登录后转到的页面。

app1/urls.py

from django.urls import path, include
from . import views
from app1.views import index 

urlpatterns = [
    path('', index, name='index'),
    path('home/', views.city_detail,  views.DemoView, name='home'),
    path('home/', views.DemoView, name='home'),
]

app1/views.py

from django.shortcuts import render, redirect

# Simple Combobox
def DemoView(request):
  color_choices = ("Red", "Blue", "Black", "Orange")
  message = ''
  if request.method == "POST":
    picked = request.POST.get('color')
    if picked == 'Red':
      message = "<<< You chose red"
      print(message)
    elif picked == 'Blue':
        message = "<<< You chose blue"
    elif picked == 'Black':
        message = "<<< You chose black"
    else:
        message = "<<< Oh no, you chose orange"
  context = {'message': message, 'color_choices': color_choices}
  return render(request, 'app1/home.html', context)

# Combobox from Database
def city_detail(request):
    form = SelectCityForm()
    object = None
    if request.method == "POST":
        form = SelectCityForm(request.POST)
        if form.is_valid():
            city_id = form.cleaned_data['city']
            object = City.objects.get(id=city_id)

    context = {'form': form, 'object': object}
    return render(request, 'app1/home.html', context)

app1/models.py(用于数据库中的下拉列表)

from django.db import models

class City(models.Model): 
    name = models.CharField(max_length=30) 

    def __str__(self):
        return self.name

app1/forms.py

from django import forms

# Simple Combobox
class SimpleCombobox(forms.Form):
    Col1 = 'Red'
    Col2 = 'Blue'
    Col3 = 'Black'
    Col4 = 'Orange'
      
    COLOR_CHOICES = (
        (Col1, "Red"),
        (Col2, "Blue"),
        (Col3, "Black"),
        (Col4, "Orange"),
    )
    cities = forms.ChoiceField(choices=COLOR_CHOICES)

class SimpleTextbox(forms.Form):
    coverletter = forms.CharField(required=False,
              widget=forms.Textarea(
                attrs={'rows': 4, 'cols': 40}))

from .models import City

class SelectCityForm(forms.Form):
    city = forms.ModelChoiceField(queryset=City.objects.all())

    class Meta:
        model = City
        fields = ('name',)

app1/home.html

<div class="b-example-divider b-example-vr"></div>

<div class="app">

    <form action="" method="post">
      {% csrf_token %}
      {{ form }}
      <input type="submit" value="Submit">
  </form>
  {% if object %}
  Here you can embed data of your city such as the ID: {{ object.id }} or the Name: {{ object.name }}. Since you defined the __str__ method you can also
  just put {{ object }} and it will give the name.
  {% endif %}

  </div>
      
    <div class="container p-5">
      <div class="row mx-auto">
        <div class="col-6">
          <form action="" method="POST" novalidate class="form-group">
            {% csrf_token %}
            <select name="color" class="form-select" >
              {% for i in color_choices %}
              <option value="{{i}}">{{i}}</option>
              {% endfor %}
            </select>
            <textarea class="form-control" name="msg" cols="30" rows="10">{{message}}</textarea>
            <button class="btn btn-primary mt-3" type="submit">Submit</button>
          </form>
        </div>
      </div>
    </div>
英文:

In Django I have two functions contained in views.py. One is called def city_detail and the other is called def DemoView. Both return return render(request, &#39;app1/home.html&#39;, context).

In urlpatterns of url.py i would like to use both functions, but it seems I can only use 1 at a time. If I use only one of the two, then it works (only one of the two works). If use both functions i get error:

 path(&#39;home/&#39;, views.city_detail, views.DemoView, name=&#39;home&#39;),
 path(&#39;home/&#39;, views.DemoView, name=&#39;home&#39;),

I get the error:

     raiseTypeError(
TypeError: kwargs argument must be a dict, but got function.

How can I use both functions in home?

UPDATE

I update the question by writing the whole code

I believed there was no need for the code as I believed the solution was simpler. Update to improve the question. I added the code that I use in the project.

home.html is the page I go to after logging in

app1/urls.py

from django.urls import path, include
from . import views
from app1.views import index 

urlpatterns = [
    path(&#39;&#39;, index, name=&#39;index&#39;),
    path(&#39;home/&#39;, views.city_detail,  views.DemoView, name=&#39;home&#39;),
    path(&#39;home/&#39;, views.DemoView, name=&#39;home&#39;),

 ]

app1/views.py

from django.shortcuts import render, redirect

#Simple Combobox
def DemoView(request):
  color_choices = (&quot;Red&quot;,&quot;Blue&quot;,&quot;Black&quot;,&quot;Orange&quot;)
  message =&#39;&#39;
  if request.method ==&quot;POST&quot;:
    picked = request.POST.get(&#39;color&#39;)
    if picked == &#39;Red&#39;:
      message = &quot;&lt;&lt;&lt; You chose red&quot;
      print(message)
    elif picked == &#39;Blue&#39;:
        message = &quot;&lt;&lt;&lt; You chose blue&quot;
    elif picked == &#39;Black&#39;:
        message = &quot;&lt;&lt;&lt; You chose black&quot;
    else:
        message = &quot;&lt;&lt;&lt; Oh no, you chose orange&quot;
  context = {&#39;message&#39;:message,&#39;color_choices&#39;:color_choices}
  return render(request, &#39;app1/home.html&#39;, context)

#Combobox from Database
def city_detail(request):
    form = SelectCityForm()
    object = None
    if request.method == &quot;POST&quot;:
        form = SelectCityForm(request.POST)
        if form.is_valid():
            city_id = form.cleaned_data[&#39;city&#39;]
            object = City.objects.get(id=city_id)


    context = {&#39;form&#39;: form, &#39;object&#39;: object}
    return render(request, &#39;app1/home.html&#39;, context)

app1/models.py (for combobox from database)

from django.db import models

class City(models.Model): 
    name = models.CharField(max_length=30) 

    def __str__(self):
        # here you want to return the name
        # print function on an instance of this model then returns the name
        return self.name

app1/forms.py

from django import forms

#Combobox Semplice
class SimpleCombobox(forms.Form):
    Col1 = &#39;Red&#39;
    Col2 = &#39;Blue&#39;
    Col3 = &#39;Black&#39;
    Col4 = &#39;Orange&#39;
      
    COLOR_CHOICES = (
        (Col1, u&quot;Red&quot;),
        (Col2, u&quot;Blue&quot;),
        (Col3, u&quot;Black&quot;),
        (Col4, u&quot;Orange&quot;),
    )
    cities = forms.ChoiceField(choices=COLOR_CHOICES)


class SimpleTextbox(forms.Form):
    coverletter = forms.CharField(required=False,
              widget=forms.Textarea(
                # rows and colums of the textarea
                attrs={&#39;rows&#39;: 4, &#39;cols&#39;: 40}))

from .models import City

class SelectCityForm(forms.Form):
    city = forms.ModelChoiceField(queryset=City.objects.all())

    class Meta:
        model = City
        fields = (&#39;name&#39;)

app1/home.html

  &lt;div class=&quot;b-example-divider b-example-vr&quot;&gt;&lt;/div&gt;

  &lt;div class=&quot;app&quot;&gt;

    &lt;form action=&quot;&quot; method=&quot;post&quot;&gt;
      {% csrf_token %}
      {{ form }}
      &lt;input type=&quot;submit&quot; value=&quot;Submit&quot;&gt;
  &lt;/form&gt;
  {% if object %}
  Here you can embed data of your city such as the ID: {{ object.id }} or the Name: {{ object.name }}. Since you defined the __str__ method you can also
  just put {{ object }} and it will give the name.
  {% endif %}

  &lt;/div&gt;
      
    &lt;div class=&quot;container p-5&quot;&gt;
      &lt;div class=&quot;row mx-auto&quot;&gt;
        &lt;div class=&quot;col-6&quot;&gt;
          &lt;form action=&quot;&quot; method=&quot;POST&quot; novalidate class=&quot;form-group&quot;&gt;
            {% csrf_token %}
            &lt;select name=&quot;color&quot; class=&quot;form-select&quot; &gt;
              {% for i in color_choices %}
              &lt;option value=&quot;{{i}}&quot;&gt;{{i}}&lt;/option&gt;
              {% endfor %}
            &lt;/select&gt;
            &lt;textarea class=&quot;form-control&quot; name=&quot;msg&quot; cols=&quot;30&quot; rows=&quot;10&quot;&gt;{{message}}&lt;/textarea&gt;
            &lt;button class=&quot;btn btn-primary mt-3&quot; type=&quot;submit&quot;&gt;Submit&lt;/button&gt;
          &lt;/form&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

答案1

得分: 3

以下是翻译好的部分:

How can I use both functions in home?

你不应该同时使用两个函数。这没有意义。视图是一个函数,它以请求作为参数(还可以选择性地包括一些URL参数),然后将其转换为响应。

如果有两个视图,那么它应该如何组合响应?两个视图都会生成响应,那么应该选择哪个响应?

因此,视图是一个确定如何将请求映射到响应的函数,你通过向特定路径发出请求来“触发”视图。

人们常常认为路径与模板有某种关联,但事实并非如此:一个视图可以渲染零个、一个或多个模板。渲染模板只是大多数Web服务器提供的一种实用工具,用于简化生成响应的过程,但完全不需要渲染模板,或者只渲染一个模板。

你可以创建实用方法、定义上下文处理器等,使将数据传递给渲染过程更加方便,但这些不是视图,它们只是辅助函数。

因此,在这种情况下,你可以将它们与以下代码结合使用:

from django.shortcuts import redirect, render

def city_detail(request):
    form = SelectCityForm()
    object = None
    color_choices = ('Red', 'Blue', 'Black', 'Orange')
    message = ''
    if request.method == 'POST':
        picked = request.POST.get('color')
        if picked == 'Red':
            message = '<<< You chose red'
            print(message)
        elif picked == 'Blue':
            message = '<<< You chose blue'
        elif picked == 'Black':
            message = '<<< You chose black'
        else:
            message = '<<< Oh no, you chose orange'
            form = SelectCityForm(request.POST)
            if form.is_valid():
                city_id = form.cleaned_data['city']
                object = City.objects.get(id=city_id)

        context = {
            'form': form,
            'object': object,
            'message': message,
            'color_choices': color_choices,
        }
        return render(request, 'app1/home.html', context)

然而,在模板中手动渲染表单项非常不方便,这会引入大量的代码重复,而且尽管HTML表单元素看起来很容易理解,但实际上它们也有一些特殊情况(例如,如果复选框被选中,它会发送 'on',否则什么都不发送)。

因此,通常会将表单字段组合在一个表单中,或者使用不同的表单:

from .models import City

class HomeForm(forms.Form):
    Col1 = 'Red'
    Col2 = 'Blue'
    Col3 = 'Black'
    Col4 = 'Orange'

    COLOR_CHOICES = (
        (Col1, "Red"),
        (Col2, "Blue"),
        (Col3, "Black"),
        (Col4, "Orange"),
    )
    color = forms.ChoiceField(choices=COLOR_CHOICES)
    city = forms.ModelChoiceField(queryset=City.objects.all())

然后,在视图中,我们可以渲染表单并处理结果:

from django.shortcuts import redirect, render

MESSAGES = {
    'Red': '<<< You chose red',
    'Blue': '<<< You chose blue',
    'Black': '<<< You chose black',
    'Orange': '<<< Oh no, you chose orange',
}

def city_detail(request):
    object = None
    message = ''
    if request.method == 'POST':
        form = HomeForm(request.POST, request.POST)
        if form.is_valid():
            object = form.cleaned_data['city']
            message = MESSAGES.get(form.cleaned_data['color'], '')
    else:
        form = HomeForm()
    context = {
        'form': form,
        'object': object,
        'message': message,
    }
    return render(request, 'app1/home.html', context)

在模板中:

<form action="" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit">
</form>

如果这两个表单是独立的,你可以使用两个你创建和处理的表单。但通常更有意义的是,有单独的视图来处理这些表单,因为有可能两个表单都是有效的,而实际上只有一个填充了数据。

英文:

> How can I use both functions in home?

You don't. It makes no sense. A view is a function that takes as parameter the request (and optionally some url parameters), and turns that into a response.

If there are two views, then hwo should it compose a response? Both views then produce a response, so what response should be picked?

A view is thus a function that determines how to map a request to a response, and you "fire" a view by making a request to a certain path.

People often think that a path is somehow related to a template, but that is not the case: a view can render zero, one, or multiple templates. Rendering templates is just a utility most web servers offer to make generating responses easier, but it is not necessary at all to render a template, or only render one.

You can make utilty methods, define context processors, etc. that make it more convenient to pass data to the rendering process, but these are not views, there are just helper functions.

So in this case you combine these with:

<pre><code>from django.shortcuts import redirect, render

def city_detail(request):
form = SelectCityForm()
object = None
color_choices = ('Red', 'Blue', 'Black', 'Orange')
message = ''
if request.method == 'POST':
picked = request.POST.get('color')
if picked == 'Red':
message = '&lt;&lt;&lt; You chose red'
print(message)
elif picked == 'Blue':
message = '&lt;&lt;&lt; You chose blue'
elif picked == 'Black':
message = '&lt;&lt;&lt; You chose black'
else:
message = '&lt;&lt;&lt; Oh no, you chose orange'
form = SelectCityForm(request.POST)
if form.is_valid():
city_id = form.cleaned_data['city']
object = City.objects.get(id=city_id)

    context = {
        &#39;form&#39;: form,
        &#39;object&#39;: object,
        &#39;message&#39;: message,
        &#39;color_choices&#39;: color_choices,
    }
    return render(request, &#39;app1/home.html&#39;, context)&lt;/code&gt;&lt;/pre&gt;

It is however quite inconvenient to render form items in the template manually, that would introduce a lot of code repitition, and while HTML form elements look easy to understand, in fact these also have some special cases (for example a checkbox sends &#39;on&#39; if the checkbox is checked, and nothing if not).

Therefore one typically combines fields in a form, or you use different forms:

<pre><code>from .models import City

class HomeForm(forms.Form):
Col1 = 'Red'
Col2 = 'Blue'
Col3 = 'Black'
Col4 = 'Orange'

COLOR_CHOICES = (
    (Col1, u&amp;quot;Red&amp;quot;),
    (Col2, u&amp;quot;Blue&amp;quot;),
    (Col3, u&amp;quot;Black&amp;quot;),
    (Col4, u&amp;quot;Orange&amp;quot;),
)
color = forms.ChoiceField(choices=COLOR_CHOICES)
city = forms.ModelChoiceField(queryset=City.objects.all())&lt;/code&gt;&lt;/pre&gt;

In the view, we can then render the form and process the results:

<pre><code>from django.shortcuts import redirect, render

MESSAGES = {
'Red': '&lt;&lt;&lt; You chose red',
'Blue': '&lt;&lt;&lt; You chose blue',
'Black': '&lt;&lt;&lt; You chose black',
'Orange': '&lt;&lt;&lt; Oh no, you chose orange',
}

def city_detail(request):
object = None
message = ''
if request.method == 'POST':
form = <b>HomeForm(request.POST, request.POST)</b>
if form.is_valid():
object = form.cleaned_data['city']
message = <b>MESSAGES.get(form.cleaned_data['color'], '')</b>
else:
form = HomeForm()
context = {
'form': form,
'object': object,
'message': message,
}
return render(request, 'app1/home.html', context)</code></pre>

and in the template:

&lt;form action=&quot;&quot; method=&quot;post&quot;&gt;
    {% csrf_token %}
    {{ form }}
    &lt;input type=&quot;submit&quot; value=&quot;Submit&quot;&gt;
&lt;/form&gt;

you can, if the two forms are independent, work with two forms you create and handle. But usually it makes more sense to then have separate views that handle the forms, since it is possible that then both forms are valid, when only one was actually filled with data.

huangapple
  • 本文由 发表于 2023年7月13日 17:16:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/76677768.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定