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
4 Answers
Solution I believe is not yet a hack (but close to) and is not that big hassle.
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 = userOverride your widget:
def __init__(self, *args, **kwargs): self.user = None super(YourWidget, self).__init__(*args, **kwargs)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.
Comments
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
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
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."), )
requestobject and then you could use thedjango.forms.models.ModelChoiceFieldand dynamically set a queryset that returns therequest.userand presents it with theSelectwidget, 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.