在 Django CreateView 中,使用 class 方法生成要在 get_context_data() 和 form_valid() 中使用的数据

In Django CreateView use a class method to generate data to use in both get_context_data() and form_valid()

我实际上正在做一个学生项目,我尝试使用 Python Django 在网络上创建一个 roguelike 游戏。

实际上我对我的问题感到困惑,为了解释我需要做的是在受所选角色影响的角色创建中生成随机特征 class。

我想在用户开始游戏之前将此特征发送给用户。并使用我在 form_valid() 中发送给他的特征将字符保存在数据库中。

我知道我可以使用 get_context_data() 将一些信息发送到我的模板,但我不知道是否可以使用 get_context_data() 发送到模板的数据form_valid().

实际上,我成功地使用了 CreateView 来通过选择的字符 class 保存具有随机特征的字符,请参阅下面的代码。

models.py

class CharacterClass(models.Model):
    name = models.CharField(max_length=20,
                            blank=True,
                            null=True)
    minHpMax = models.PositiveIntegerField(default=10,
                                           validators=[MinValueValidator(10)],
                                           blank=False,
                                           null=False)
    minStrength = models.IntegerField(default=1,
                                      validators=[MinValueValidator(0)],
                                      blank=False,
                                      null=False)
    minAgility = models.IntegerField(default=1,
                                     validators=[MinValueValidator(0)],
                                     blank=False,
                                     null=False)
    minInt = models.IntegerField(default=1,
                                 validators=[MinValueValidator(0)],
                                 blank=False,
                                 null=False)
    minPhysResis = models.IntegerField(default=0,
                                       validators=[MinValueValidator(0)],
                                       blank=False,
                                       null=False)
    minMagRes = models.IntegerField(default=0,
                                    validators=[MinValueValidator(0)],
                                    blank=False,
                                    null=False)

    def __str__(self):
        return f'{self.id}: {self.name}'

    def generateHpMax(self):
        return random.randint(self.minHpMax, self.minHpMax + 10)

    def generateStrength(self):
        return random.randint(self.minStrength, self.minStrength + 10)

    def generateAgility(self):
        return random.randint(self.minAgility, self.minAgility + 10)

    def generateIntelligence(self):
        return random.randint(self.minInt, self.minInt + 10)

    def generatePR(self):
        return random.randint(self.minPhysResis, self.minPhysResis + 10)

    def generateMR(self):
        return random.randint(self.minMagRes, self.minMagRes + 10)


class Character(models.Model):
    name = models.CharField(max_length=20,
                            default='Jon Doe',
                            blank=False,
                            null=False)
    characterClass = models.ForeignKey(CharacterClass,
                                       on_delete=models.CASCADE,
                                       related_name='characterClass')
    level = models.PositiveIntegerField(default=1,
                                        validators=[MinValueValidator(1)],
                                        blank=False,
                                        null=False)
    hpMax = models.PositiveIntegerField(default=10,
                                        validators=[MinValueValidator(0)],
                                        blank=False,
                                        null=False)
    hp = models.PositiveIntegerField(default=10,
                                     validators=[MinValueValidator(0)],
                                     blank=False,
                                     null=False)
    strength = models.IntegerField(default=1,
                                   validators=[MinValueValidator(0)],
                                   blank=False,
                                   null=False)
    agility = models.IntegerField(default=1,
                                  validators=[MinValueValidator(0)],
                                  blank=False,
                                  null=False)
    intelligence = models.IntegerField(default=1,
                                       validators=[MinValueValidator(0)],
                                       blank=False,
                                       null=False)
    physicalResistance = models.IntegerField(default=0,
                                             validators=[
                                                 MinValueValidator(0)],
                                             blank=False,
                                             null=False)
    magicalResistance = models.IntegerField(default=0,
                                            validators=[
                                                MinValueValidator(0)],
                                            blank=False,
                                            null=False)
    inventory = models.OneToOneField('Inventory',
                                     on_delete=models.PROTECT)

    def __str__(self):
        return f'{self.id}: {self.name} ' \
               f'[Lvl: {self.level}' \
               f'|Class: {self.characterClass}' \
               f'|HpM: {self.hpMax}' \
               f'|hp: {self.hp}' \
               f'|Str: {self.strength}' \
               f'|Ag: {self.agility}' \
               f'|Int: {self.intelligence}' \
               f'|Pr: {self.physicalResistance}' \
               f'|Mr: {self.magicalResistance}]'

    def get_absolute_url(self):
        return reverse('characterDetail', kwargs={'pk': self.pk})

views.py

    class GenerateCharacterView(CreateView):
    model = Character
    form_class = CharacterForm
    template_name = 'characterForm.html'

    def form_valid(self, form):
        # Creation of Character without db saving
        self.object = form.save(commit=False)

        # creation of empty inventory unique for the Character
        inventory = Inventory()
        inventory.save()
        self.object.inventory = inventory

        pkCharacterClass= form['characterClass'].value()
        currentCharacterClass = get_object_or_404(CharacterClass,
                                                  pk=pkCharacterClass)

        # for a CharacterClass found, get random characteristics for the Character in creation
        generatedHpMax = currentCharacterClass.generateHpMax()
        self.object.hpMax = generatedHpMax
        self.object.hp = generatedHpMax

        generatedStrength = currentCharacterClass.generateStrength()
        self.object.strength = generatedStrength

        generatedAgility = currentCharacterClass.generateAgility()
        self.object.agility = generatedAgility

        generatedIntelligence = currentCharacterClass.generateIntelligence()
        self.object.intelligence = generatedIntelligence

        generatedPhysicalResistance = currentCharacterClass.generatePR()
        self.object.physicalResistance = generatedPhysicalResistance

        generatedMagicalResistance = currentCharacterClass.generateMR()
        self.object.magicalResistance = generatedMagicalResistance

        # Enregistrement en BDD de l'objet et appel du super form valid pour
        # renvoie de la succes url défini en Model
        self.object.save()
        return super().form_valid(form)

forms.py

    class CharacterForm(forms.ModelForm):
    class Meta:
        model = Character
        # exclude = []
        fields = ['name', 'characterClass']

    name = forms.CharField(max_length=20, widget=widgets.TextInput(
        attrs={'placeholder': 'Enter Your Name'}
    ))

urls.py

    urlpatterns = [
    path('admin/', admin.site.urls),
    path('', IndexView.as_view(), name='home'),
    path('generateCharacter',
         GenerateCharacterView.as_view(),
         name='generateCharacter'),
    path('characterDetail/<int:pk>', CharacterDetailView.as_view(),
         name='characterDetail'),
]

这种用法你已经成功了吗? 你知道它是否有效吗?如何 ? 你有没有给我一些提示或建议?我现在正在研究它 2 天,我快要疯了。


你好抱歉这张票,我可能在里面显示的有误。

现在我使用我在立交桥问题单中显示的代码。

此时我可以通过选择一个CharacterClass和一个角色名称来生成一个具有随机特征的角色。

但是我想做的是当我进入表单页面时生成随机特征。

所以我尝试在页面上放置一个调用路径 'createCharacter/1' 的按钮,其中“1”是我角色的 pk class。

此时我尝试生成随机特征,以在提交表单之前向用户显示特征。

而且表格里只问了角色名。

所以这是我之前使用的代码

urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', IndexView.as_view(), name='home'),
    path('createCharacter/<int:characterClass>',
         CreateCharacterView.as_view(),
         name='createCharacter'),
    path('characterDetail/<int:pk>', CharacterDetailView.as_view(),
         name='characterDetail'),
]

views.py

class CreateCharacterView(CreateView):
    model = Character
    form_class = CharacterForm
    template_name = 'characterForm.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        classCharacter = get_object_or_404(CharacterClass ,self.kwargs['characterClass'])
        context['randomCarac'] = classCharacter.getRadomCarac()
        return context 

    def form_valid(self, form):
        self.object = form.save(commit=False)


        inventory = Inventory()
        inventory.save()
        self.object.inventory = inventory


        self.object.characterClass= context['characterClass']
        self.object.hpMax = context['hpMax ']
        self.object.hp = context['hpMax ']
        self.object.strength = context['strength ']
        self.object.agility = context['agility ']
        self.object.intelligence = context['intelligence  ']
        self.object.physicalResistance = context['physicalResistance ']
        self.object.magicalResistance = context['magicalResistance ']

        self.object.save()
        return super().form_valid(form)

models.py

class CharacterClass(models.Model):
    name = models.CharField(max_length=20,
                            blank=True,
                            null=True)
    minHpMax = models.PositiveIntegerField(default=10,
                                           validators=[MinValueValidator(10)],
                                           blank=False,
                                           null=False)
    minStrength = models.IntegerField(default=1,
                                      validators=[MinValueValidator(0)],
                                      blank=False,
                                      null=False)
    minAgility = models.IntegerField(default=1,
                                     validators=[MinValueValidator(0)],
                                     blank=False,
                                     null=False)
    minInt = models.IntegerField(default=1,
                                 validators=[MinValueValidator(0)],
                                 blank=False,
                                 null=False)
    minPhysResis = models.IntegerField(default=0,
                                       validators=[MinValueValidator(0)],
                                       blank=False,
                                       null=False)
    minMagRes = models.IntegerField(default=0,
                                    validators=[MinValueValidator(0)],
                                    blank=False,
                                    null=False)

    def __str__(self):
        return f'{self.id}: {self.name}'

    def generateHpMax(self):
        return random.randint(self.minHpMax, self.minHpMax + 10)

    def generateStrength(self):
        return random.randint(self.minStrength, self.minStrength + 10)

    def generateAgility(self):
        return random.randint(self.minAgility, self.minAgility + 10)

    def generateIntelligence(self):
        return random.randint(self.minInt, self.minInt + 10)

    def generatePR(self):
        return random.randint(self.minPhysResis, self.minPhysResis + 10)

    def generateMR(self):
        return random.randint(self.minMagRes, self.minMagRes + 10)

    def getRadomCarac(self):
        return {'hpMax': self.generateHpMax(),
                'strength': self.generateStrength(),
                'agility': self.generateAgility(),
                'intelligence': self.generateIntelligence(),
                'physicalResistance': self.generatePR(),
                'magicalResistance': self.getRadomCarac()}


那是行不通的。 看来我无法访问 form_validation()

中的上下文 ['something']

所以我也试过

class CreateCharacterView(CreateView):
    model = Character
    form_class = CharacterForm
    template_name = 'characterForm.html'
    classCharacter = get_object_or_404(CharacterClass ,self.kwargs['characterClass'])
    randomCarac = classCharacter.getRadomCarac()

def form_valid(self, form):
        # Do things here with randomCarac['something']

想要在视图中使用值 'global',get_object_or_404() 不起作用

如果您想在向用户显示表单时创建一个随机字符并保留该随机字符以在用户提交表单时保存(字符的名称),你需要以某种方式保留随机字符。

您有两个选择:

  1. 在第一次使用 GET 请求(使用空白名称)呈现表单时创建并保存角色,并将角色的 ID 作为隐藏字段包含在表单中,这样当您提交您 更新 具有名称的角色的表格。 您需要检查提交的 id 是否以某种方式属于 "draft" 角色,以确保用户不会窃取另一个已经存在的角色,因此您可能需要添加 status 字段到您的 Character 模型。您不会在此处使用 CreateView,但基于函数的视图会更合适。

  2. 您在视图的 get() 方法中为字符创建值并将它们存储在 request.session 中(参见 here),以便您可以在 form_valid() 方法中从会话中检索所述值。这种方法对我来说似乎更容易。您仍然可以在此处使用 CreateView。您可以直接使用 {{ request.session.<somekey> }} 或通过将它们添加到 get_context_data() 中的上下文中以更容易在模板中呈现的格式在您的模板中显示它们。

通过隐藏的表单输入字段提交随机值不是一种选择,因为任何人都可以随时篡改提交的值。

请注意,我不明白为什么您的 Character 模型没有引用 UserForeignKeyOneToOneField)。目前尚不清楚如何检查角色 是否属于此用户 并避免用户窃取其他人的角色。如果你添加这个,那么在选项 1 中你甚至不需要检查 id 因为 request.user 只能有一个 "draft" Character.

可能会遗漏您的解决方案中的某些内容,我已经尝试了解决方案 2,并且我将代码写在下面

index.html

    <button>
        <a href="{% url 'generateCharacterByPk' 1 %}">Play2(pk=1)</a>
    </button>

    <button>
        <a href="{% url 'generateCharacterByPk' 2 %}">Play2(pk=2)</a>
    </button>

urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', IndexView.as_view(), name='home'),
    path('generateCharacterByPk/<int:pk>',
         GenerateCharacterView2.as_view(),
         name='generateCharacterByPk'),
    path('generateCharacter',
         GenerateCharacterView.as_view(),
         name='generateCharacter'),
    path('characterDetail/<int:pk>', CharacterDetailView.as_view(),
         name='characterDetail'),
]

forms.py

class CharacterForm2(forms.ModelForm):
    class Meta:
        model = Character
        # exclude = []
        fields = ['name']

    name = forms.CharField(max_length=20, widget=widgets.TextInput(
        attrs={'placeholder': 'Enter Your Name'}
    ))

views.py

class GenerateCharacterView2(CreateView):
    model = Character
    form_class = CharacterForm2
    template_name = 'characterForm.html'

    def get_context_data(self, **kwargs):
        result = super().get_context_data(**kwargs)
        result['title'] = 'Create Character'
        return result

    def get(self, request, *args, **kwargs):
        request.session.characterClass = self.kwargs['pk']
        currentCharacterClass = get_object_or_404(CharacterClass,
                                                  pk=self.kwargs['pk'])
        request.session.characterClass = currentCharacterClass
        request.session.HpMax = currentCharacterClass.generateHpMax()
        request.session.Strength = currentCharacterClass.generateStrength()
        request.session.Agility = currentCharacterClass.generateAgility()
        request.session.Intelligence = currentCharacterClass.generateIntelligence()
        request.session.PhysicalResistance = currentCharacterClass.generatePR()
        request.session.MagicalResistance = currentCharacterClass.generateMR()
        return render(request, 'characterForm.html')

    def form_valid(self, form):
        # Création de l'objet sans enregistrement en base
        self.object = form.save(commit=False)

        # Création d'un inventaire vide unique au personnage avec affectation
        inventory = Inventory()
        inventory.save()

        self.object.inventory = inventory
        self.object.hpMax = request.session.characterClass
        self.object.hpMax = request.session.HpMax
        self.object.hp = request.session.HpMax
        self.object.strength = request.session.Strength
        self.object.agility = request.session.Agility
        self.object.intelligence = request.session.Intelligence
        self.object.physicalResistance = request.session.PhysicalResistance
        self.object.magicalResistance = request.session.MagicalResistance

        self.object.save()
        return super().form_valid(form)

characterForm.html

{% extends 'base.html' %}

{% block page-top %}


    <form method="POST">
        {% csrf_token %}
        {% for field in form %}
            <div class="h5 font-weight-bold text-primary text-uppercase mb-1">
                <label>
                    {{ field.label }}:
                </label>
                <nav>
                    {{ field }}
                </nav>
            </div>
        {% endfor %}

        {{ request.session.characterClass }}
        {{ request.session.HpMax }}
        {{ request.session.Strength }}
        {{ request.session.Intelligence }}
        {{ request.session.Agility }}
        {{ request.session.PhysicalResistance }}
        {{ request.session.MagicalResistance }}
        <button type="submit">Valider</button>
    </form>


{% endblock %}

当我访问 generateCharacterByPk/1 或 generateCharacterByPk/2

时,我的反应很奇怪

首先,我在带有提交按钮的页面上看到了由我的 get 随机生成的随机特征。

当我点击重新加载页面时,我最终看到了我的表单和一个提交按钮,但我在我的页面上再也看不到这些功能了。

当我验证表单时,出现以下错误:

AttributeError at /generateCharacterByPk/1
module 'django.http.request' has no attribute 'session'.
Request Method: POST
Request URL: http://127.0.0.1:8000/generateCharacterByPk/1
Django Version: 3.0.2
Exception Type: AttributeError
Exception Value:    
module 'django.http.request' has no attribute 'session'.
Exception Location: C:\Users\u049298\Desktop\PYTHON_DJANGO\projetWebRPG\app\views.py in form_valid, line 130
```

好的,这是在我的项目中工作的最终代码

url.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', IndexView.as_view(), name='home'),
    path('generateCharacterByPk/<int:pk>', GenerateCharacterView2.as_view(),
         name='generateCharacterByPk'),
    path('generateCharacter', GenerateCharacterView.as_view(),
         name='generateCharacter'),
    path('characterDetail/<int:pk>', CharacterDetailView.as_view(),
         name='characterDetail'),
]

forms.py

class CharacterForm2(forms.ModelForm):
    class Meta:
        model = Character
        # exclude = []
        fields = ['name']

    name = forms.CharField(max_length=20, widget=widgets.TextInput(
        attrs={'placeholder': 'Enter Your Name'}
    ))

views.py

class GenerateCharacterView2(CreateView):
    model = Character
    form_class = CharacterForm2
    template_name = 'characterForm.html'

    def get_context_data(self, **kwargs):
        result = super().get_context_data(**kwargs)
        result['title'] = 'Create Character'
        return result

    def get(self, *args, **kwargs):
        currentCharacterClass = get_object_or_404(CharacterClass,
                                                  pk=self.kwargs['pk'])

        self.request.session['characterClass'] = self.kwargs['pk']
        self.request.session['HpMax'] = currentCharacterClass.generateHpMax()
        self.request.session['Strength'] = currentCharacterClass.generateStrength()
        self.request.session['Agility'] = currentCharacterClass.generateAgility()
        self.request.session['Intelligence'] = currentCharacterClass.generateIntelligence()
        self.request.session['PhysicalResistance'] = currentCharacterClass.generatePR()
        self.request.session['MagicalResistance'] = currentCharacterClass.generateMR()
        return super().get(self)

    def form_valid(self, form):
        # Création de l'objet sans enregistrement en base
        self.object = form.save(commit=False)

        # Création d'un inventaire vide unique au personnage avec affectation et récupéraction de la classe du personnage
        currentCharacterClass = get_object_or_404(CharacterClass,
                                                  pk=self.request.session['characterClass'])
        inventory = Inventory()
        inventory.save()

        # Constitution du personnage
        self.object.inventory = inventory
        self.object.characterClass = currentCharacterClass
        self.object.hpMax = self.request.session['HpMax']
        self.object.hp = self.request.session['HpMax']
        self.object.strength = self.request.session['Strength']
        self.object.agility = self.request.session['Agility']
        self.object.intelligence = self.request.session['Intelligence']
        self.object.physicalResistance = self.request.session['PhysicalResistance']
        self.object.magicalResistance = self.request.session['MagicalResistance']

        # Création en BDD du personnage
        self.object.save()
        return super().form_valid(form)

characterForm.html

{% extends 'base.html' %}

{% block page-top %}


    <form method="POST">
        {% csrf_token %}
        {% for field in form %}
            <div class="h5 font-weight-bold text-primary text-uppercase mb-1">
                <label>
                    {{ field.label }}:
                </label>
                <nav>
                    {{ field }}
                </nav>
            </div>
        {% endfor %}

        {{ request.session.characterClass }}
        {{ request.session.HpMax }}
        {{ request.session.Strength }}
        {{ request.session.Intelligence }}
        {{ request.session.Agility }}
        {{ request.session.PhysicalResistance }}
        {{ request.session.MagicalResistance }}
        <button type="submit">Valider</button>
    </form>


{% endblock %}

感谢@dirkgroten 的帮助,真的很有帮助