How to place a django form in a nav bar so that it appears on every page?

こ雲淡風輕ζ 提交于 2020-04-16 02:27:33

问题


I have a form whose queryset depends on request.user, and whose initial value depends on a session key. The primary models are User (slight modification of default User model) and Account, with a many-to-many relationship between them. The form allows a User to change the Account that he/she is viewing, and that choice must persist as the User navigates the site. The form works fine when created in a single view and passed to a single template, but I want the form to appear in the top navigation bar so that the User can change Accounts from anywhere.

Here is the form:

class ChangeAccountContextForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user')
        self.current_account_id = kwargs.pop('account_id')
        super(ChangeAccountContextForm, self).__init__(*args, **kwargs)
        self.fields['account_choices'].queryset = self.user.accounts.all()
        try:
            self.fields['account_choices'].initial = Account.objects.get(id=self.current_account_id)
        except(Account.DoesNotExist):
            self.fields['account_choices'].initial = None
    #queryset and initial are set to None, because they are assigned dynamically in the constructor (see above)
    account_choices = forms.ModelChoiceField(queryset=None, initial=None, label='Account:', widget=forms.Select(attrs={'onChange':'this.form.submit()', 'class': 'custom-select mr-sm-2 ml-2'}), required=True )

    class Meta:
        model = User
        fields = ['account_choices']

And here is the existing view where the form is used:

@login_required
def welcome_view(request):
    user = request.user
    context = {}
    accounts = user.accounts.all().order_by('account_name')
    context['accounts'] = accounts
    context['num_accounts'] = len(accounts)

    try:
        account_id = request.session['current_account_id']
    except (KeyError):
        account_id = None

    if request.method == 'POST':
        form = ChangeAccountContextForm(request.POST, user=user, account_id=account_id)
        context['form'] = form
        if form.is_valid():
            new_account_context = form.cleaned_data['account_choices']           
            request.session['current_account_name'] = new_account_context.account_name
            request.session['current_account_id'] = new_account_context.id

    else:
        form = ChangeAccountContextForm(user=user, account_id=account_id)
        context['form'] = form

    return render(request, 'welcome.html', context)

(The session keys, incidentally, are set when the User logs in.)

Given the dependence on request.user and variables stored in the session, I'm not sure how to include the form on every page without reconstructing the form in every view as show above. That would work, I suppose, but I'm sure there must be a more DRY approach.


回答1:


This is a partial answer to my own question. I'm adding it here because I think the documentation for adding a form to a custom template tag is lacking.

1.) Create folder called templatetags (with __init__.py, of course) and add account_context_form.py (or whatever name makes sense for you). Mine looks like:

from django import template
from userauth.forms import ChangeAccountContextForm

register = template.Library()

@register.inclusion_tag('account_context_form.html')
def account_context_form(**kwargs):
    user = kwargs['user']
    account_id = kwargs['account_id']
    form = ChangeAccountContextForm(user=user, account_id=account_id)
    return {'account_context_form' : form }
  1. create a html file that you're passing to your tag and save to your templates folder. In my case, 'account_context_form.html'
<form class="form-inline" action="{{ request.path }}" method="POST">{% csrf_token %}
    {{ account_context_form }}
</form>
  1. in base_generic.html (or where ever make sense for you), call the tag like so:
{% load account_context_form %}
{% account_context_form user=request.user account_id=request.session.current_account_id %}

As for how to actually handle the POST request, see my question (and my answer) here: How to handle a POST request from a Django form loaded via a custom template tag?



来源:https://stackoverflow.com/questions/60660373/how-to-place-a-django-form-in-a-nav-bar-so-that-it-appears-on-every-page

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!