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

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)

Related

Django pass list to form to create Choices

I want to create a ChoiceField on a form that has choices from a list passed to it by a view.
from django import forms
class OrderForm(forms.Form):
product_choices = []
def __init__(self, products=None, *args, **kwargs):
super(OrderForm, self).__init__(*args, **kwargs)
if products:
print(products)
choices = enumerate(products)
product_name = forms.ChoiceField(label='Product', choices=choices)
Not sure how to use the init function to achieve this?
The above will not work, since the choices you here define will be taken from a variable named choices at construction of the class.
You can however generate:
from django import forms
class OrderForm(forms.Form):
product_name = forms.ChoiceField(label='Product', choices=[])
def __init__(self, products=None, *args, **kwargs):
super(OrderForm, self).__init__(*args, **kwargs)
if products:
self.fields['product_name'].choices = [
(str(k), v)
for k, v in enumerate(products))
]
You thus then construct an OrderForm and pass a list (or any iterable of strings) through the products parameter, like:
def some_view(request):
form = OrderForm(products=['product A', 'product B'])
# ...
# return some HttpResponse

Example of custom model field extending ForeignKey

Can someone give me an example of extending a ForeignKey model field? I tried like this:
class ForeignKeyField(forms.ModelChoiceField):
def __init__(self, *args, **kwargs):
super(ForeignKeyField, self).__init__(Chain.objects.all(), *args, **kwargs)
def clean(self, value):
return Chain.objects.get(pk=value)
class CustomForeignKey(models.ForeignKey):
description = "key from ndb"
__metaclass__ = models.SubfieldBase
def __init__(self, *args, **kwargs):
super(CustomForeignKey, self).__init__(*args, **kwargs)
def db_type(self, connection):
return "ndb"
def to_python(self, value):
# import pdb; pdb.set_trace()
from google.appengine.api.datastore_types import Key
if isinstance(value, Key) is True:
return value.id()
if value is None:
return
return value
def get_db_prep_save(self, value, connection, prepared=False):
save_value = ndb.Key(API_Chain, value.id).to_old_key()
return save_value
def formfield(self, **kwargs):
return models.Field.formfield(self,ForeignKeyField, **kwargs)
I don't know why but if i use __metaclass__ = models.SubfieldBase the to_python gets called with None values and it says that foreign key cannot be null. If I inherit from models.Field it works but not as a foreign Key.
I would like to see how one can extend the functionality of models.ForeignKey. Thanks.
Do you need SubfieldBase? It does some magic behind the scenes so that the field has a descriptor that calls to_python. ForeignKey has different kind of descriptor. I guess ForeignKey descriptor is overridden by the subfieldbase descriptor. In other words, they arenot compatible.

Django: How can I use get_model in my use case to correctly import a class and avoid conflicts

I have a model with a get_form method.
# models.py
from model_utils.managers import InheritanceManager
from breathingactivities.forms import ParentRecordForm, ChildRecordForm
class ParentRecord(models.Model):
....
objects = InheritanceManager()
def get_fields(self, exclude=('user')):
fields = self._meta.fields
return [(f.verbose_name, f.value_to_string(self)) for f in fields if not exclude.__contains__(f.name)]
#classmethod
def get_form(self, *args, **kwargs):
return ParentRecordForm
class ChildRecord(ParentRecord):
....
duration = DurationField(
_('Duration'),
help_text=_('placeholder'))
#classmethod
def get_form(self, *args, **kwargs):
return ChildRecordForm
I have a view that uses this get_form method to determine to correct form for a given object.
# views.py
class ParentRecordUpdateView(UpdateView):
model = ParentRecord
form_class = ParentRecordForm
template_name = 'parentrecords/create.html'
def get_object(self, **kwargs):
return ParentRecord.objects.get_subclass(slug=self.kwargs['slug'])
def get_form_class(self, *args, **kwargs):
form_class = self.model.objects.get_subclass(slug=self.kwargs['slug']).get_form()
return form_class
def get_form_kwargs(self):
kwargs = super(ParentRecordUpdateView, self).get_form_kwargs()
kwargs.update({'user': self.request.user})
return kwargs
I am using InheritanceManager from django-model-utils so I get a clean API to subclasses when I query a parent class - that is the get_subclass() stuff, and this is why my view works with ParentRecord.
All this works good. I see via console that indeed form_class is the form class I'd expect, for example, ChildRecordForm when the instance is of ChildRecord.
In my forms.py, I can't just import models.ParentRecord and models.ChildRecord, as I import those forms in my models.py, and thus an error is raised that Django can't import these models. I presume because of circular imports.
So, I try this instead:
# forms.py
from django.db.models import get_model
class ParentRecordForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super (ParentRecordForm, self).__init__(*args, **kwargs)
class Meta:
model = get_model('model', 'ParentRecord')
exclude = ('user')
class ChildRecordForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super (ChildRecordForm, self).__init__(*args, **kwargs)
class Meta:
model = get_model('model', 'ParentRecord')
exclude = ('user')
However, model for these forms always returns None.
I I go and pass ChildRecordForm some totally unrelated model that I can import from a different app in my project, for example:
# forms.py
from another_app import AnotherModel
class ChildRecordForm(forms.ModelForm):
def __init__(self, user, *args, **kwargs):
super (ChildRecordForm, self).__init__(*args, **kwargs)
class Meta:
model = AnotherModel
exclude = ('user')
Then it works, meaning, The form returns the fields of AnotherModel.
So, I can't work out why get_model() works for me in shell, but not in a form class when I use it to declare a value for a model.
My guess on get_model() is that running it at the class-definition level can give incorrect results, since it will get evaulated as Django is populating all the models in it's AppCache. My quick reading of the django.db.models.loading module doesn't seem to show that issue, but one thing to try is to run get_model() inside a view and print out the results to see if it is what you think it should be, since by that time the AppCache should be fully loaded.
But - as a workaround to get around the original circular import (so you don't have to use get_model anyway) is to not do the form imports at the module level - you can stick them in the classmethod instead:
class ParentRecord(models.Model):
#classmethod
def get_form(self, *args, **kwargs):
from yourapp.forms import BreathingActivityRecordForm
return BreathingActivityRecordForm
This way, the import will only be evaulated when you actually call .get_form(), and there shouldn't be any circular dependencies at module loading tme.

Django - Can't remove empty_label from TypedChoiceField

I have field in my model:
TYPES_CHOICES = (
(0, _(u'Worker')),
(1, _(u'Owner')),
)
worker_type = models.PositiveSmallIntegerField(max_length=2, choices=TYPES_CHOICES)
When I use it in ModelForm it has "---------" empty value. It's TypedChoiceField so it hasn't empty_label attribute., so I can't override it in form init method.
Is there any way to remove that "---------"?
That method doesn't work too:
def __init__(self, *args, **kwargs):
super(JobOpinionForm, self).__init__(*args, **kwargs)
if self.fields['worker_type'].choices[0][0] == '':
del self.fields['worker_type'].choices[0]
EDIT:
I managed to make it work in that way:
def __init__(self, *args, **kwargs):
super(JobOpinionForm, self).__init__(*args, **kwargs)
if self.fields['worker_type'].choices[0][0] == '':
worker_choices = self.fields['worker_type'].choices
del worker_choices[0]
self.fields['worker_type'].choices = worker_choices
The empty option for any model field with choices determined within the .formfield() method of the model field class. If you look at the django source code for this method, the line looks like this:
include_blank = self.blank or not (self.has_default() or 'initial' in kwargs)
So, the cleanest way to avoid the empty option is to set a default on your model's field:
worker_type = models.PositiveSmallIntegerField(max_length=2, choices=TYPES_CHOICES,
default=TYPES_CHOICES[0][0])
Otherwise, you're left with manually hacking the .choices attribute of the form field in the form's __init__ method.
self.fields['xxx'].empty_value = None would not work If you field type is TypedChoiceField which do not have empty_label property.
What should we do is to remove first choice:
class JobOpinionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(JobOpinionForm, self).__init__(*args, **kwargs)
for field_name in self.fields:
field = self.fields.get(field_name)
if field and isinstance(field , forms.TypedChoiceField):
field.choices = field.choices[1:]
Try:
def __init__(self, *args, **kwargs):
super(JobOpinionForm, self).__init__(*args, **kwargs)
self.fields['worker_type'].empty_value = None
https://docs.djangoproject.com/en/1.3/ref/forms/fields/#typedchoicefield

Django - How to extend a form?

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'