I have a selectfield which I override a charfield in my model. I can't use a foreignkey at all on the field.
class AliasForm(forms.ModelForm):
account = forms.ModelChoiceField(queryset=Account.objects.all(), widget=forms.HiddenInput())
domain = forms.ModelChoiceField(queryset=Domain.objects.all(), widget=forms.HiddenInput())
end = forms.ModelChoiceField(queryset=Mailbox.objects.all())
def __init__(self, *args, **kwargs):
self.account = kwargs.pop('account', None)
super(AliasForm, self).__init__(*args, **kwargs)
if self.account:
self.fields['end'].queryset = Mailbox.objects.filter(account=self.account)
How can I make end get passed in a value where it is autoselected?
Ok figured it out.
I needed to pass in initial but with instance.id and not instance
form = AliasForm(initial={'end': mailbox.id})
I was doing just 'end': mailbox
Related
class Model_Neural_form(forms.ModelForm):
allMod = forms.ModelChoiceField(queryset=Model_Neural.objects.all())
class Meta:
model = Model_Neural
fields = ["nom_mod", "modl"]
def __init__(self, *args, **kwargs):
super(Model_Neural_form, self).__init__(*args, **kwargs)
self.fields['allMod'].label = ''
If you want to set the default initial value you should be defining initial like other form fields.
You need to set initial when you create your form like this:
allMod = forms.ModelChoiceField(
initial=instance.pk if instance else None,
queryset=Model_Neural.objects.all()
)
I use inlineformset_factory with a custom form option in order to change the queryset and the widget of a m2m field, ie: ezMap. I want the form to give the user the option to add or remove the current selected_map to the m2m field with CheckBoxSelectMultiple widget. However, I dont want to give the user the ability to remove other objects that were already there. The problem is when I save the formset with formset.save_m2m(), it overides the field and erase all objects that were already saved.
How could I just add a new object without erasing others?
models: (some of unecessary fields were removed)
class Shapefile(models.Model):
filename = models.CharField(max_length=255)
class EzMap(models.Model):
map_name = models.SlugField(max_length=50)
layers = models.ManyToManyField(Shapefile, verbose_name='Layers to display', null=True, blank=True)
class LayerStyle(models.Model):
styleName = models.SlugField(max_length=50)
layer = models.ForeignKey(Shapefile)
ezMap = models.ManyToManyField(EzMap)
forms:
class polygonLayerStyleFormset(forms.ModelForm):
add_to_map = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
self.map_selected = kwargs.pop("map_selected", None)
super(polygonLayerStyleFormset, self).__init__(*args, **kwargs)
self.fields['conditionStyle'].help_text = "Put * if you want to select the entire table"
self.fields['ezMap'].widget = forms.CheckboxSelectMultiple()
self.fields['ezMap'].queryset = EzMap.objects.filter(id=self.map_selected.id)
self.fields['ezMap'].help_text =""
class Meta:
model = LayerStyle
def save(self, *args, **kwargs):
instance = super(polygonLayerStyleFormset, self).save(*args, **kwargs)
instance.add_to_map = self.cleaned_data['add_to_map']
return instance
ftlStylePolygonFormset = inlineformset_factory(Shapefile, LayerStyle, can_delete=True, extra=1, max_num=5,
fields = ['styleName', 'conditionStyle', 'fillColor', 'fillOpacity', 'strokeColor', 'strokeWeight', 'ezMap'], form=polygonLayerStyleFormset)
views:
def setLayerStyle(request, map_name, layer_id):
map_selected = EzMap.objects.get(map_name=map_name, created_by=request.user)
layer_selected = Shapefile.objects.get(id=layer_id)
layerStyle_selected = LayerStyle.objects.filter(layer=layer_selected)
styleFormset = ftlStylePolygonFormset
if request.POST:
formset = styleFormset(request.POST, instance=layer_selected)
if formset.is_valid():
instances = formset.save()
for instance in instances:
if instance.add_to_map:
instance.ezMap.add(map_selecte)
else:
instance.ezMap.remove(map_selected)
save_link = u"/ezmapping/map/%s" % (map_name)
return HttpResponseRedirect(save_link)
else:
formset = styleFormset(instance=layer_selected)
#set initial data for add_to_map
for form in formset:
if form.instance.pk:
if map_selected in form.instance.ezMap.all():
form.fields['add_to_map'].initial = {'add_to_map': True}
I am confused as to what you're doing with the ezMap form field. You set its queryset to a single-element list, then use a CheckboxSelectMultiple widget for it. Are you setting up to let the user deselect that matching map, but not add new ones?
To do this at initialization, you need to define a custom base formset class and pass that in as the formset argument to your factory.
from django.forms.models import BaseInlineFormSet
class polygonLayerStyleForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.map_selected = kwargs.pop("map_selected", None)
super(polygonLayerStyleForm, self).__init__(*args, **kwargs)
self.fields['conditionStyle'].help_text = "Put * if you want to select the entire table"
self.fields['ezMap'].widget = forms.CheckboxSelectMultiple()
self.fields['ezMap'].queryset = EzMap.objects.filter(id=self.map_selected.id)
self.fields['ezMap'].help_text =""
class Meta:
model = LayerStyle
class polygonLayerStyleFormset(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
self.map_selected = kwargs.pop("map_selected", None)
super(polygonLayerStyleFormset, self).__init__(*args, **kwargs)
def _construct_form(self, i, **kwargs):
kwargs['map_selected'] = self.map_selected
return super(polygonLayerStyleFormset, self)._construct_form(i, **kwargs)
ftlStylePolygonFormset = inlineformset_factory(Shapefile, LayerStyle, formset=polygonLayerStyleFormset, form=polygonLaterStyleForm, # and other arguments as above
)
It might be simpler to just go through the formset forms and directly change the field's queryset after creating it in your view:
formset = ftlStylePolygonFormset(instance=layer_selected)
for form in formset.forms:
form.fields['ezMap'].queryset = EzMap.objects.filter(id=map_selected.id)
Speaking of which, the usual convention is to split the POST and GET cases in the view:
from django.shortcuts import render
def setLayerStyle(request, map_name, layer_id):
map_selected = EzMap.objects.get(map_name=map_name, created_by=request.user)
layer_selected = Shapefile.objects.get(id=layer_id)
layerStyle_selected = LayerStyle.objects.filter(layer=layer_selected)
if request.method == 'POST':
formset = ftlStylePolygonFormset(request.POST, instance=layer_selected, map_selected=map_selected)
if formset.is_valid():
instances = formset.save()
save_link = u"/ezmapping/map/%s" % (map_name)
return HttpResponseRedirect(save_link)
else:
formset = ftlStylePolygonFormset(instance=layer_selected, map_selected=map_selected)
return render(request, "ezmapping/manage_layerStyle.html", {'layer_style': layerStyle_selected, 'layerStyleformset': formset, 'layer': layer_selected})
And it's better to use the redirect shortcut to reverse lookup a view for your redirect on success rather than hardcode the target URL. And to use get_object_or_404 or some equivalent when accessing objects based on URL arguments - right now a bogus URL will trigger an exception and give the user a 500 status error, which is undesirable.
To conditionally add to the ezMap relationship:
class polygonLayerStyleForm(forms.ModelForm):
add_to_map = forms.BooleanField()
def save(self, *args, **kwargs):
instance = super(polygonLayerStyleForm, self).save(*args, **kwargs)
instance.add_to_map = self.cleaned_data['add_to-map']
return instance
Then in the view:
instances = formset.save()
for instance in instances:
if instance.add_to_map:
instance.ezMap.add(map_selected)
You could also do the add call in the save method, but then you'd have to set the map as member data sometime previously - and more importantly, deal with the commit=False case.
I have a simple form,
class Compose(forms.Form):
CHOICES = ()
recepient = forms.ChoiceField(choices=CHOICES)
subject = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Textarea)
Chocies are generated as
def mychoiceview(request):
subscribed_lists, other_lists = getListOfLists(request.user.email)
for lst in subscribed_lists:
# list name and list address
CHOICES = CHOICES + ((lst[1],lst[0]),)
#Bind data to form and render
Basically, the user is subscribed to certain lists (from a superset of lists) and can choose (via dropdown) which list he/she wants to send the message to.
The problem is that I cannot find how to bind the "CHOICES" to the django form.
Some solutions online include using models.. but I don't have a queryset... just a dynamically generated tuple of ids I want the choicefield to render.
Any ideas ?
Thanks !
#nnmware's solution is correct, but a little too complex/confusing. Below is a more succinct version that works just as well:
class DynamicBindingForm(forms.Form):
def __init__(self, *args, **kwargs):
super(DynamicBindingForm, self).__init__(*args, **kwargs)
self.fields['recipient'] = forms.ChoiceField(choices=db_lookup_choices())
where db_lookup_choices is a call to some database or other set of dynamic data and returns a list of pairs for choices: [('val', 'Label'),('val2', 'Label2')]
If you using Class-based view, then:
in view make mixin for sending request in form
class RequestToFormMixin(object):
def get_form_kwargs(self):
kwargs = super(RequestToFormMixin, self).get_form_kwargs()
kwargs.update({'request': self.request})
return kwargs
class YouView(RequestToFormMixin, CreateView):
model = YouModel
# etc
in form make mixin for receive request from view
class RequestMixinForm(forms.Form):
def __init__(self, *args, **kwargs):
request = kwargs.pop('request')
super(RequestMixinForm, self).__init__(*args, **kwargs)
self._request = request
class Compose(RequestMixinForm):
subject = forms.CharField(max_length=100)
message = forms.CharField(widget=forms.Textarea)
def __init__(self, *args, **kwargs):
super(Compose, self).__init__(*args, **kwargs)
subscribed_lists, other_lists = getListOfLists(self._request.user.email)
for lst in subscribed_lists:
# list name and list address
CHOICES = CHOICES + ((lst[1],lst[0]),)
self.fields['recipient'] = forms.ChoiceField(choices=CHOICES)
I want to display a form with some customized user data in it. More specifically I want to fill a forms.ChoiceField with different data for each user.
This is my Form:
class WallPostForm(forms.Form):
text = forms.CharField(label=u'', widget=TinyMCE(attrs={'cols': 70, 'rows': 5}))
relates_to = forms.ChoiceField(label=u'Relates to', choices=[], widget=forms.Select(), required=False)
def __init__(self, data):
self.fields['relates_to'] = forms.ChoiceField(label=u'Relates to', choices=data, widget=forms.Select(), required=False)
super(WallPostForm, self).__init__()
And this is how I am calling this:
user = get_object_or_404(User, username=username)
data = UserTopics.objects.filter(user=user, result=0).values('id', 'topic__name')[:10]
form = WallPostForm(data)
I get a 'WallPostForm' object has no attribute 'fields' error.
What am I doing wrong?
As an addition to Jack's answer, you're probably better off just replacing the choices attribute, rather than the whole field:
def __init__(self, *args, **kwargs):
relates_to_choices = kwargs.pop('relates_to_choices')
super(WallPostForm, self).__init__(*args, **kwargs)
self.fields['relates_to'].choices = relates_to_choices
(I renamed the variable, it won't be a queryset.)
Django sets up the form's fields property in the __init__.
So just swap your code:
def __init__(self, data):
super(WallPostForm, self).__init__()
self.fields['relates_to'] = forms.ChoiceField(label=u'Relates to', choices=data, widget=forms.Select(), required=False)
Though, you probably shouldn't override a Form's __init__ like that. Django's form system expects the data arg in init to contain the data for the form, not a queryset you're using for a choices field.
I'd override it differently:
def __init__(self, *args, **kwargs):
relates_to_queryset = kwargs.pop('relates_to_queryset')
super(WallPostForm, self).__init__(*args, **kwargs)
self.fields['relates_to'] = forms.ChoiceField(label=u'Relates to', choices=relates_to_queryset, widget=forms.Select(), required=False)
Then call it:
form = WallPostForm(request.POST or None, relates_to_queryset=data)
You can use "initial" parameter, see the docs: https://docs.djangoproject.com/en/dev/ref/forms/api/#django.forms.Form.initial
I want to populate my modelform to include the IP address, however I'm not able to set the ip as I want, it simply returns "This field is required" for IP address.
models.py
class SignUp(models.Model):
....
ip = models.IPAddressField()
views.py
def my_view(request):
form = SignUpForm(request.POST, ip_addr=get_client_ip(request))
forms.py
class SignUpForm(ModelForm):
class Meta:
model = SignUp
def __init__(self, *args, **kwargs):
self.ip_addr = kwargs.pop('ip_addr', None)
super(SignUpForm, self).__init__(*args, **kwargs)
def clean_ip(self):
return self.ip_addr
You haven't actually set an initial value for the ip form field meaning it's empty. Try:
def __init__(self, *args, **kwargs):
super(SignUpForm, self).__init__(*args, **kwargs)
self.fields['ip'].initial = kwargs.pop('ip_addr', None)
The clean_ip method is for validation upon submission of the form and is only called after form.is_valid() is called. It should check that the value of the field is a valid ip
I don't think you should use clean_ip here. You are not cleaning it anyhow. Either use initial in your view or override save method.