Django - How to extend a form? - django

I'm new to Django. I have installed an external App that is in "python2.6/site-packages/haystack". This external App have "generic forms" but I need to add a CSS class that is not in the "generic form".
How can I extend the "forms.py" the "class FacetedModelSearchForm" from the "generic form" to "forms.py" in my own App?
Here is the code from the "generic form"
class SearchForm(forms.Form):
q = forms.CharField(required=False, label=_('Search'))
def __init__(self, *args, **kwargs):
self.searchqueryset = kwargs.pop('searchqueryset', None)
self.load_all = kwargs.pop('load_all', False)
if self.searchqueryset is None:
self.searchqueryset = SearchQuerySet()
super(SearchForm, self).__init__(*args, **kwargs)
def no_query_found(self):
"""
Determines the behavior when no query was found.
By default, no results are returned (``EmptySearchQuerySet``).
Should you want to show all results, override this method in your
own ``SearchForm`` subclass and do ``return self.searchqueryset.all()``.
"""
return EmptySearchQuerySet()
def search(self):
if not self.is_valid():
return self.no_query_found()
if not self.cleaned_data.get('q'):
return self.no_query_found()
sqs = self.searchqueryset.auto_query(self.cleaned_data['q'])
if self.load_all:
sqs = sqs.load_all()
return sqs
def get_suggestion(self):
if not self.is_valid():
return None
return self.searchqueryset.spelling_suggestion(self.cleaned_data['q'])
class FacetedSearchForm(SearchForm):
def __init__(self, *args, **kwargs):
self.selected_facets = kwargs.pop("selected_facets", [])
super(FacetedSearchForm, self).__init__(*args, **kwargs)
def search(self):
sqs = super(FacetedSearchForm, self).search()
# We need to process each facet to ensure that the field name and the
# value are quoted correctly and separately:
for facet in self.selected_facets:
if ":" not in facet:
continue
field, value = facet.split(":", 1)
if value:
sqs = sqs.narrow(u'%s:"%s"' % (field, sqs.query.clean(value)))
return sqs
How can I add to the field "q" the CSS class "myspecialcssclass" extending this class in my App "forms.py"? The class that I need to extend is the "FacetedSearchForm". Any clues?

from haystack.forms import FacetedSearchForm
class CustomSearchForm(FacetedSearchForm)
q = forms.CharField(required=False, label='Search', widget=forms.widgets.TextInput(attrs={"class":"myspecialcssclass",}))
your custom form must be set in your haystack urls e.g:
from haystack.views import SearchView
urlpatterns = patterns('haystack.views',
url(r'^$', SearchView(form_class=CustomSearchForm, results_per_page=20), name='haystack_search'),
)
Also see the haystack views and forms documentation

I think this:
https://docs.djangoproject.com/en/dev/ref/forms/widgets/#customizing-widget-instances
might help.
Basically, you need to subclass FacetedSearchForm and add an argument to you widget
class MyForm(FacetedSearchForm):
q = forms.CharField(
required=False,
label='Search',
widget=forms.TextInput(attrs={'class':'myspecialcssclass'}))
And that should be it.

The form field widget attrs maps html attributes to their values. Override these attributes in a subclasses __init__ function to safely modify the field.
class MyForm(FacedSearchForm):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['q'].widget.attrs['class'] = 'myspecialcssclass'

Related

Default value in Django Model

I have a model named "Product" and it has an attribute like price, quantity, and remarks.
Thru the models.py, if remarks has a property of "null=True", it will return a value "None" but I want it to be a dash(-). If you will be adding a "default='-'" into the remarks column in the model, once its form is created and loaded, it has a dash('-') on it but I want nothing on the form when it's loaded. Do you have any ideas if that's possible?
Maybe you should try a clean method on the form.
def clean_<property>(self):
property = self.cleaned_data['property']
if not property:
return "-"
I haven't tested the code but it should work out
https://docs.djangoproject.com/en/4.0/ref/forms/validation/#cleaning-a-specific-field-attribute
I think you can set the custom initial value of the form
class ProductForm(forms.ModelForm):
... fields here ...
def __init__(self, *args, **kwargs):
... other code ...
initial = kwargs.pop('initial', {})
remark_value = initial.get('remarks')
initial.update("remarks", "" if remark_value == "-" else remark_value)
kwargs['initial'] = initial
super(ProductForm, self).__init__(*args, **kwargs)
You have more options but here is 2 you can do:
If you want set default="-" you have to override the form __int__() method
so the form would looks like
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
#creating
self.fields['your_field'].initial = " "
# updating
if self.instance.pk:
self.fields['your_field'].initial = self.instance.your_field
If you do not want set default you have to override the model save() method
class MyModel(models.Model):
def save(self, *args, **kwargs):
if not self.your_field:
self.your_field = "-"
return super(MyModel, self).save(*args, **kwargs)

Django: Current User Id for ModelForm Admin

I want for filter a ModelChoiceField with the current user. I found a solution very close that I want to do, but I dont understand
Django: How to get current user in admin forms
The answer accepted says
"I can now access the current user in my forms.ModelForm by accessing self.current_user"
--admin.py
class Customer(BaseAdmin):
form = CustomerForm
def get_form(self, request,obj=None,**kwargs):
form = super(Customer, self).get_form(request, **kwargs)
form.current_user = request.user
return form
--forms.py
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.filter(tenant=????))
class Meta:
model = fa_customers
How do I get the current user on modelchoice queryset(tenant=????)
How do I call the self.current_user in the modelform(forms.py)
Override __init__ constructor of the CustomerForm:
class CustomerForm(forms.ModelForm):
...
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['default_tax'].queryset =
fa_tax_rates.objects.filter(tenant=self.current_user))
Queryset in the form field definition can be safely set to all() or none():
class CustomerForm(forms.ModelForm):
default_tax = forms.ModelChoiceField(queryset=fa_tax_rates.objects.none())
Just to sum up the solution because it was very hard for me to make this work and understand the accepted answer
In admin.py
class MyModelForm (forms.ModelForm):
def __init__(self, *args,**kwargs):
super (MyModelForm ,self).__init__(*args,**kwargs)
#retrieve current_user from MyModelAdmin
self.fields['my_model_field'].queryset = Staff.objects.all().filter(person_name = self.current_user)
#The person name in the database must be the same as in Django User, otherwise use something like person_name__contains
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
def get_form(self, request, *args, **kwargs):
form = super(MyModelAdmin, self).get_form(request, *args, **kwargs)
form.current_user = request.user #get current user only accessible in MyModelAdminand pass it to MyModelForm
return form

How can I make all CharField in uppercase direct in model?

I tried to use UpperCase in all my CharField, in all my Django Model.
Today I have some code in my save method:
def save(self, *args, **kwargs):
for field_name in ['razao_social', 'nome_fantasia', 'cidade', 'endereco','bairro', 'uf', 'cli_parc_nomeparc', 'cli_repr_nomerepr']:
val = getattr(self, field_name, False)
if val:
setattr(self, field_name, val.upper())
super(Pessoa, self).save(*args, **kwargs)
But its take some time. There`s any method to put some uppercase=True in my models?
Thanks.
Here is how to override a Django Model Field and make it upper-case as of Django 1.8.
This will:
work by saving the upper-cased value to the database
returns an upper-cased value in the save response.
Here's the code:
from django.db import models
class UpperCaseCharField(models.CharField):
def __init__(self, *args, **kwargs):
super(UpperCaseCharField, self).__init__(*args, **kwargs)
def pre_save(self, model_instance, add):
value = getattr(model_instance, self.attname, None)
if value:
value = value.upper()
setattr(model_instance, self.attname, value)
return value
else:
return super(UpperCaseCharField, self).pre_save(model_instance, add)
If you want to do this in Django rest framework, here's the code:
from rest_framework import serializers
class UpperCaseSerializerField(serializers.CharField):
def __init__(self, *args, **kwargs):
super(UpperCaseSerializerField, self).__init__(*args, **kwargs)
def to_representation(self, value):
value = super(UpperCaseSerializerField, self).to_representation(value)
if value:
return value.upper()
The correct way would be to define custom model field:
from django.db import models
from django.utils.six import with_metaclass
class UpperCharField(with_metaclass(models.SubfieldBase, models.CharField)):
def __init__(self, *args, **kwargs):
self.is_uppercase = kwargs.pop('uppercase', False)
super(UpperCharField, self).__init__(*args, **kwargs)
def get_prep_value(self, value):
value = super(UpperCharField, self).get_prep_value(value)
if self.is_uppercase:
return value.upper()
return value
and use it like so:
class MyModel(models.Model):
razao_social = UpperCharField(max_length=50, uppercase=True)
# next field will not be upper-cased by default (it's the same as CharField)
nome_fantasia = UpperCharField(max_length=50)
# etc..
you also need to resolve south migration issues (if necessary), by adding this code:
from south.modelsinspector import add_introspection_rules
add_introspection_rules([
(
[UpperCharField],
[],
{
"uppercase": ["uppercase", {"default": False}],
},
),
], ["^myapp\.models\.UpperCharField"])
(path in the last line depends on the field class localization. Please read the south docs for explanation.)
Although there's a small downside when you use shell for instance to create model object and save it in variable:
my_object = MyModel.objects.create(razao_social='blah')
print my_object.razao_social
you won't get upper-cased value. You need to retrieve the object from the database. I will update this post, when I find out how to resolve this issue as well.
Instead of defining a custom field, you can also use the RegexValidator:
from django.core.validators import RegexValidator
...
my_field = models.CharField(
max_length=255,
validators=[RegexValidator('^[A-Z_]*$',
'Only uppercase letters and underscores allowed.')],
)
(see Docs)
Here is my dirty and easier solution without having to deal with migrations:
char_fields = [f.name for f in self._meta.fields if isinstance(f, models.CharField) and not getattr(f, 'choices')]
for f in char_fields:
val = getattr(self, f, False)
if val:
setattr(self, f, val.upper())
super(Cliente, self).save(*args, **kwargs)
django 4, just override the save() method of the model
from django.db import models
class MyModel(models.Model):
my_field = models.CharField(max_length=255)
def save(self, *args, **kwargs):
self.my_field = self.my_field.upper()
super().save(*args, **kwargs)

Binding data to django's form choicefield

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)

Django forms request.user

I my model users can create rifles and this rifle is obviously associated with a User.
class Gun(ImageModel):
user = models.ForeignKey(User)
...
...
...
I have another model which is dependent on this and need to make use of the users rifles, but when the user adds a record I only want to display his rifles.
mt model looks as follows
class Trophies(ImageModel):
used_his = models.ForeignKey(Gun)
my form looks as follows
from django.forms import ModelForm
from django import forms
from models import Trophies
from gunsafe.models import Gun
from django.contrib.auth.models import User
class TrophiesForm(request.user, ModelForm):
used_his = forms.ModelMultipleChoiceField(queryset=Gun.objects.filter(user__id=1))
def __init__(self, user, *args, **kwargs):
super(TrophiesForm, self).__init__(*args, **kwargs)
self.fields['used_his'].queryset = User.objects.filter(pk = user)
I was wondering how I can get the current logged in users ID instead of the user__id=1
Here is the view.
def edit(request, trophy_id, template_name='trophies/edit.html'):
trophy = Trophies.objects.get(pk=trophy_id)
if request.method == 'POST':
form = TrophiesForm(request.POST, request.FILES, instance=trophy)
if form.is_valid():
newform = form.save(commit=False)
newform.user = request.user
newform.save()
...
...
I think you can achieve this by overriding the __init__() method of the form, passing in an instance of User and filtering the queryset using that user. Something like this:
class TrophiesForm(ModelForm):
used_his = forms.ModelMultipleChoiceField(queryset=Gun.objects.filter(user__id=1))
def __init__(self, user, *args, **kwargs):
super(TrophiesForm, self).__init__(*args, **kwargs)
self.fields['used_his'].queryset = User.objects.filter(pk = user.id)
In your view you can pass in the appropriate (currently logged in) instance of User.
def my_trophies(request, *args, **kwargs):
user = request.user
form = TrophiesForm(user)
...
Another angle to Manoj's submission ...
use a kwarg to pass user data, as to not mess with the method signature since it expects request.POST as the first argument. A better convention would be as follows.
class TrophiesForm(ModelForm):
def __init__(self, *args, **kwargs):
#using kwargs
user = kwargs.pop('user', None)
super(TrophiesForm, self).__init__(*args, **kwargs)
self.fields['used_his'].queryset = User.objects.filter(pk = user.id)
Now in the call, this is more explicit and follows a better signature convention
form = TrophiesForm(request.POST, request.FILES, user=request.user)
You could also use instance: (note the super before you grab the instance obj)
class TrophiesForm(ModelForm):
def __init__(self, *args, **kwargs):
super(SubmissionUploadForm, self).__init__(*args, **kwargs)
user = self.instance.user
self.fields['used_his'].queryset = User.objects.filter(pk = user.id)
You can then call in your views.py like so:
form = TrophiesForm(instance=*MyModel*(user=request.user))