简体   繁体   English

在 Django CreateView 中,使用类方法生成要在 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()

I'm actually working on a student project which i try to create a roguelike game on web using Python Django.我实际上正在研究一个学生项目,我尝试使用 Python Django 在网络上创建一个 roguelike 游戏。

I'm actully confusing by my issue, to explain what i need to do is generate random characteristics in a character creation impacted by a choosen character class .我实际上对我的问题感到困惑,为了解释我需要做的是在受所选角色类影响的角色创建中生成随机特征。

I want to send this characteristics to the user before he start the game.我想在用户开始游戏之前将这些特征发送给用户。 And use the characteristics I send him in my form_valid() to save the character in db.并使用我在 form_valid() 中发送给他的特征将字符保存在 db 中。

I know that I a can use get_context_data() to send some information to my template but I don't know if can use the data send by get_context_data() to the template in form_valid().我知道我可以使用 get_context_data() 将一些信息发送到我的模板,但我不知道是否可以使用 get_context_data() 发送到 form_valid() 中的模板的数据。

Actually I'm succed with the a CreateView to save a character with random characteristics by a choosen character class, see the code bellow.实际上,我使用 CreateView 成功地通过选择的字符类保存了具有随机特征的字符,请参阅下面的代码。

models.py模型.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视图.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表格.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网址.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'),
]

Did you already succeed this kind of use ?这种用法你已经成功了吗? Did you know if it's working ?你知道它是否有效吗? and how ?如何 ? Did you had some tips or advices for me ?你对我有什么提示或建议吗? I'm working on it form 2 days now and i'm going crazy.我现在正在研究它 2 天,我快疯了。


Hello sorry for the this ticket, I may a mistake in what I show in it.您好,很抱歉这张票,我可能在其中显示的内容有误。

For now i use the code that I show in ticket for overpass my issue.现在我使用我在票证中显示的代码来解决我的问题。

At this moment i can generate a character with random characteristics by choising a CharacterClass and a character name.此时我可以通过选择一个 CharacterClass 和一个角色名称来生成一个具有随机特征的角色。

But what i want to do is generate the random characteristics when I come in the form page.但是我想要做的是当我进入表单页面时生成随机特征。

So i try to put on a page a button who call a path 'createCharacter/1' where '1' is the pk of my character class.因此,我尝试在页面上放置一个按钮,该按钮调用路径“createCharacter/1”,其中“1”是我的角色类的 pk。

And i tried to generate the radom characteristics at this moment to show to the user the characteristics before the submit of the form.并且我此时尝试生成随机特性以在提交表单之前向用户显示特性。

And in the form only asked the character name.并且在表格中只询问了人物姓名。

So this was the code that I used previously所以这是我之前使用的代码

urls.py网址.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视图.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模型.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()}


That doesn't work.那行不通。 It seems i don't have acces to my context['something'] in the form_validation()似乎我无法访问 form_validation() 中的上下文 ['something']

So i also tried to所以我也试着

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']

to have the value 'global' in the View to use when i want, the get_object_or_404() don't work要在视图中使用值“全局”以在我需要时使用,get_object_or_404() 不起作用

Ok guys this is the final code who work in my project好的,这是在我的项目中工作的最终代码

url.py网址.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表格.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视图.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字符表.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 %}

Thx @dirkgroten for your help, it was realy helpfull谢谢@dirkgroten 的帮助,这真的很有帮助

If you want to create a random character when the form is shown to the user and keep that random character to save when the user submits the form (the name of the character), you'll need to persist the random character somehow.如果您想在向用户显示表单时创建一个随机字符在用户提交表单时保存该随机字符(字符的名称),则需要以某种方式保留随机字符。

You have a couple of options:你有几个选择:

  1. Create and save the character already when rendering the form the first time with the GET request (with a blank name) and include the id of the character in the form as hidden field so that when you submit the form you update the character with the name.在第一次使用 GET 请求(名称为空)呈现表单时创建并保存该角色,并将该角色的 id 作为隐藏字段包含在表单中,以便在提交表单时使用名称更新该角色. You'll want to check that submitted id belongs to a "draft" character somehow in order to be sure the user doesn't steal another already existing character, so you might need to add a status field to your Character model.您需要以某种方式检查提交的id属于“草稿”角色,以确保用户不会窃取另一个已经存在的角色,因此您可能需要在Character模型中添加一个status字段。 You would not use the CreateView here but a function-based view would be more appropriate.您不会在此处使用CreateView ,但基于函数的视图会更合适。

  2. You create the values for the character in the get() method of your view and store them in the request.session (see here ) so that you can retrieve said values from the session in the form_valid() method.您可以在视图的get()方法中为角色创建值,并将它们存储在request.session (参见 此处)中,以便您可以在form_valid()方法中从会话中检索所述值。 This method seems easier to me.这种方法对我来说似乎更容易。 You can still use the CreateView here.您仍然可以在此处使用CreateView You can display them in your template either directly using {{ request.session.<somekey> }} or by adding them to your context in get_context_data() in a format that might be easier to render in your template.您可以直接使用{{ request.session.<somekey> }}将它们显示在您的模板中,也可以通过在get_context_data()中以更容易在模板中呈现的格式将它们添加到您的上下文中。

Submitting the random values via hidden form input fields is not an option, because anyone can always tamper the values submitted.通过隐藏的表单输入字段提交随机值不是一种选择,因为任何人都可以篡改提交的值。

Note that I don't understand why your Character model doesn't have a reference to the User ( ForeignKey or OneToOneField ).请注意,我不明白为什么您的Character模型没有对User的引用( ForeignKeyOneToOneField )。 It's not clear how you can check that the character belongs to this user and avoid a user stealing someone else's character.目前尚不清楚如何检查角色是否属于该用户并避免用户窃取其他人的角色。 If you add this, then in option 1 you don't even need to check for an id since request.user can only have one "draft" Character .如果你添加这个,那么在选项 1 中你甚至不需要检查id因为request.user只能有一个“草稿” Character

May miss something in your solution, I've tried the solution 2, and i write the code bellow可能会遗漏您的解决方案中的某些内容,我已经尝试了解决方案 2,并且我编写了以下代码

index.html索引.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网址.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表格.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视图.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字符表.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 %}

And I have a weird reaction when I access to generateCharacterByPk/1 or generateCharacterByPk/2当我访问 generateCharacterByPk/1 或 generateCharacterByPk/2 时,我有一个奇怪的反应

First I see the random features randomly generated by my get on my page with a submit button.首先,我使用提交按钮在我的页面上看到随机生成的随机特征。

When I click on the reload page and I end up with my form and a submit button but I don't see the features on my page anymore.当我点击重新加载页面时,我最终会看到我的表单和一个提交按钮,但我在我的页面上看不到这些功能了。

And when I validate the form, I get the following error:当我验证表单时,我收到以下错误:

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
```

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM