5

How would I pass the request.user object into the render function of a custom widget I have created, or what is the best way to access the current user from within the render function

1
  • Can you give us some more context? You could use a custom form constructor to pass in the request object and then you could use the django.forms.models.ModelChoiceField and dynamically set a queryset that returns the request.user and presents it with the Select widget, but I'm guessing you need something more custom for the presentation, possibly a non-editable field who's value is provided by the form. Commented Mar 10, 2011 at 11:58

4 Answers 4

10

Solution I believe is not yet a hack (but close to) and is not that big hassle.

  1. Override init on your forms:

    def __init__(self, *args, **kwargs):
        user = kwargs.pop('user', None)
        super(SomeForm, self).__init__(*args, **kwargs)
        self.fields['your_field'].widget.user = user
    
  2. Override your widget:

    def __init__(self, *args, **kwargs):
        self.user = None
        super(YourWidget, self).__init__(*args, **kwargs)
    
  3. Make profit with your user on a widget.

Obviously you need provide user to a form, and this on some edge cases might be a problem, for example you are using django dynamic views and you hate to change them to somewhat more static approach.

Sign up to request clarification or add additional context in comments.

Comments

2

It is not straight forward to have widgets customized based on the user. Widget is an entity in the presentation layer and should not be aware of the request context. You should customize the form to take one of the multiple widgets, or their changed parameters.

Creating dynamic forms is very well documented.

Comments

1

I have run into this as well. Sometimes there just no getting around it. Note that this is a bit hacky, but it works. I feel a little icky using it, but it's one of those annoying cross-cutting-concerns, and the Django people decided that it is never, ever fine for form fields, widgets, or filters to ever have access to the request context.

If this is in somelibrary/templatetags/widgethack.py:

from django import template
register = template.Library()

class _WidgetContextWrapper(object):
  def __init__( self, widget=None, context=None ):
    self.widget  = widget
    self.context = context
  def __getattr__( self, attr ):
    return getattr( self.widget, attr )
  def render( self, name, value, attrs=None ):
    try:
      return self.widget.render( name, value, attrs=attrs, context=self.context )
    except TypeError:
      return self.widget.render( name, value, attrs=attrs )

def contextfield( context, field ):
  return field.as_widget( widget=_WidgetContextWrapper( field.field.widget, context ) ) 

register.simple_tag(takes_context=True)(contextfield)

Then you can access it in a template via:

{% load widgethack %}
{% contextfield some_field %}

Then you just need to make sure the field you want has a widget that overrides render to accept the additional context= argument:

from django import forms
from django.template.loader import render_to_string
from django.template.context import Context

class MyWidget( forms.widgets.TextInput ):
  def render( self, name, value, attrs=None, context=None ):
    attrs   = attrs   if attrs   else {}
    context = context if context else Context()
    context.push()
    context["do_what"]="you_want"
    rendered = render_to_string("just_for_example.html",context)
    context.pop()
    return rendered

And use that widget:

from django import forms
from wherever import MyWidget

class MyModelForm( forms.ModelForm )
  my_field = forms.CharField( widget=MyWidget )
  ...

Alas, the one big failing I find in Django is that a lot of classes really need to be class factories that allow you to override inner classes like BoundField, ModelFormOptions, and the classes that formset_factory et al return. :-P </rant>

Comments

1

The question is very old but I just had this same problem and the solution I found seems good. The widget below is just a textfield with some instructions:

class TextInputWithInstructions(TextInput):

    def __init__(self, *args, **kwargs):
        self.instruction_text = kwargs['attrs'].pop('instruction_text')
        super(TextInputWithInstructions, self).__init__(*args, **kwargs)

    def render(self, name, value, attrs=None):
        base_output = super(TextInputWithInstructions, self).render(name=name, value=value, attrs=attrs)
        if self.instruction_text is not None:
            base_output = '<p class="form-control-static">' + str(self.instruction_text) + '</p>' + base_output
        return mark_safe(base_output)

Then the problem was passing the instruction_text . which Is solved like this:

email_address = MultiEmailFieldString(label=_("Email address"), required=False,
                                      widget=TextInputWithInstructions(
                                          attrs={'instruction_text':
                                                     'Email notifications will be sent to addresses listed below.'}),
                                      help_text=_("Separate multiple addresses with a comma."), )

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.