django positiveinteger default value of blank - django

I have a models class with a select list / positive integer field.
class AchievementDetails(models.Model, FillableModelWithLanguageVersion):
ACADEMIC_ACHIEVEMENT = 1
COMMERCIAL_ACHIEVEMENT = 2
PERSONAL_ACHIEVEMENT = 3
PROFESSIONAL_ACHIEVEMENT = 4
SPORTING_ACHIEVEMENT = 5
OTHER_ACHIEVEMENT_TYPE = 6
ACHIEVEMENT_TYPES = (
(ACADEMIC_ACHIEVEMENT, _('Academic Details')),
(COMMERCIAL_ACHIEVEMENT, _('Commercial Achievement')),
(PERSONAL_ACHIEVEMENT, _('Personal Achievement')),
(PROFESSIONAL_ACHIEVEMENT, _('Professional Achievement')),
(SPORTING_ACHIEVEMENT, _('Sporting Achievement')),
(OTHER_ACHIEVEMENT_TYPE, _('Other Achievement Type')),
)
....
achievement_type = models.PositiveIntegerField(choices=ACHIEVEMENT_TYPES)
....
The above set up adds in the following select list option as the default option:
<option value="">---------</option>
I have been instructed not to include SELECT_TYPE = '' into the ACHIEVEMENT_TYPES.
I am using django-parsley for client side validation, so the option value must be a blank value.
I have two questions:
1. How do I replace the default text of '---------' with 'Select Achievement Type'
2. How do I remove the default option value when the form is in the edit template?

I got this to work using the:
validators=[MinValueValidator(1)]
This is how I used it:
Include the import in the models.py file:
from django.core.validators import MinValueValidator
Include the types with the zero value included to the models.py file:
SELECT_ACHIEVEMENT_TYPE = 0
ACADEMIC_ACHIEVEMENT = 1
COMMERCIAL_ACHIEVEMENT = 2
PERSONAL_ACHIEVEMENT = 3
PROFESSIONAL_ACHIEVEMENT = 4
SPORTING_ACHIEVEMENT = 5
OTHER_ACHIEVEMENT_TYPE = 6
WRITE_MY_OWN_ACHIEVEMENT_TYPE_DESCRIPTION = 7777 # 7777 triggers a hidden text field to be displayed.
DISPLAY_ONLY_ACHIEVEMENT_DESCRIPTION_WITH_PROMPT = 8888
DISPLAY_ONLY_ACHIEVEMENT_DESCRIPTION_WITHOUT_PROMPT = 9999
ACHIEVEMENT_TYPES = (
(SELECT_ACHIEVEMENT_TYPE, _('Select Type')),
(ACADEMIC_ACHIEVEMENT, _('Academic Achievement')),
(COMMERCIAL_ACHIEVEMENT, _('Commercial Achievement')),
(PERSONAL_ACHIEVEMENT, _('Personal Achievement')),
(PROFESSIONAL_ACHIEVEMENT, _('Professional Achievement')),
(SPORTING_ACHIEVEMENT, _('Sporting Achievement')),
(OTHER_ACHIEVEMENT_TYPE, _('Other Achievement Type')),
(WRITE_MY_OWN_ACHIEVEMENT_TYPE_DESCRIPTION, _('Write my own Type description')),
(DISPLAY_ONLY_ACHIEVEMENT_DESCRIPTION_WITH_PROMPT, _('Display only Description with prompt')),
(DISPLAY_ONLY_ACHIEVEMENT_DESCRIPTION_WITHOUT_PROMPT, _('Display only Description without prompt'))
)
Include the field in the models.py file with the choices, default and MinValueValidator:
....
achievement_type = models.PositiveIntegerField(choices=ACHIEVEMENT_TYPES, default=SELECT_ACHIEVEMENT_TYPE, validators=[MinValueValidator(1)])
....
In the forms.py file, include the error_messages meta data to override the error message (for Django 1.5+):
.....
error_messages = {
'achievement_type': {'validate_min': _('This field is required.')},
}
....

Related

Django: get choices key from display value

Let's say I have the following Django model:
class Person(models.Model):
SHIRT_SIZES = (
(0, 'Small'),
(1, 'Medium'),
(2, 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.IntegerField(choices=SHIRT_SIZES)
I can create create a Person instance and get the shirt_size display value very easily:
john = Person(name="John", shirt_size=2)
john.shirt_size # 2
john.get_shirt_size_display() # 'Medium'
How can I do this the other way? That is, given a shirt size of Medium, how can I get the integer value? I there a method for that or should I write my own method on the Person object like so:
class Person(models.Model):
...
#staticmethod
def get_shirt_size_key_from_display_value(display_value):
for (key, value) in Person.SHIRT_SIZES:
if value == display_value:
return key
raise ValueError(f"No product type with display value {display_value}")
The docs recommend the following:
class Person(models.Model):
SMALL = 0
MEDIUM = 1
LARGE = 2
SHIRT_SIZES = (
(SMALL, 'Small'),
(MEDIUM, 'Medium'),
(LARGE, 'Large'),
)
name = models.CharField(max_length=60)
shirt_size = models.IntegerField(choices=SHIRT_SIZES)
Now the name MEDIUM is attached to your model and model instances:
>>> john = Person(name="John", shirt_size=2)
>>> john.shirt_size
2
>>> john.MEDIUM
2
If given a string, you can use getattr:
def get_shirt_size(instance, shirt_size):
return getattr(instance, shirt_size.upper())
choices_dict = {y: x for x, y in shirt_size.choices}
will give you a dictionary with all the ints as values and sizes as keys.
So you can write a function that returns the int of whatever shirt size you put in, or make choices_dict part of your Person object.

Django conditional annotation: Queryset return empty if `When` condition return empty

I have three model Flat, FlatDebit and FlatDebitType
class Flat(models.Model):
number = models.IntegerField()
class FlatDebit(models.Model):
flat = models.ForeingKey('foo.Flat', related_name='debits')
debit_type = models.ForeingKey('foo.FlatDebitType')
unpaid_amount = models.DecimalField()
class FlatDebitType(models.Model):
TYPE1 = 1
TYPE2 = 2
TYPE_CHOICES = ((TYPE1, 'Type 1'), (TYPE2, 'Type 2'),)
type = models.PositiveSmallIntegerField(default=TYPE1, choices=TYPE_CHOICES)
I want to get sum of unpaid_amount of some debits of each flat by debit_type.
Following annotation works as expected if there is debits which has FlatDebitType which has TYPE2.
flats = Flat.objects.all()
# <QuerySet [<Flat: 1>,..]
flats = flats.annotate(
# type1_unpaid_amount=Sum(....),
type2_unpaid_amount=Sum(Case(
# If there is no debit which has debit_type=TYPE2
# `flats` queryset return empty
When(debits__debit_type__type=FlatDebitType.TYPE2,
then='debits__unpaid_amount'
),
output_field=models.DecimalField(),
# I specified default value for getting 0 as value
# if debits with TYPE1 but not affecting
default=Decimal('0.00')
)),
)
But if there is no debits which specified debit_type, queryset returns empty.
print(flats)
#<QuerySet []>
I'm sorry for my bad English.

Is there a way to sort a dictionary and keeping its keys?

I have made an "Enums" class like this:
class PersonneEnums(object):
LANGUE_ALBANAIS = 0
LANGUE_ALLEMAND = 1
LANGUE_ANGLAIS = 2
LANGUE_ARABE = 3
LANGUE_ARMENIEN = 4
LANGUE_BENGALI = 5
LANGUE_CATALAN = 6
LANGUE_CHINOIS = 7
LANGUE_COREEN = 8
LANGUE_CROATE = 9
LANGUE_DANOIS = 10
LANGUE_ESPAGNOL = 11
LANGUE_FINNOIS = 12
LANGUE_FRANCAIS = 13
LANGUE_GREC = 14
LANGUE_HONGROIS = 15
LANGUE_ITALIEN = 16
LANGUE_MALAIS = 17
LANGUE_MONGOL = 18
LANGUE_NEERLANDAIS = 19
LANGUE_OCCITAN = 20
LANGUE_PERSAN = 21
LANGUE_PORTUGAIS = 22
LANGUE_ROUMAIN = 23
LANGUE_RUSSE = 24
LANGUE_SERBE = 25
LANGUE_SLOVAQUE = 26
LANGUE_SLOVENE = 27
LANGUE_SUEDOIS = 28
LANGUE_TURC = 29
LANGUE_AUTRE = 30
TAB_LANGUE = {
LANGUE_ALBANAIS: _(u'Albanian'),
LANGUE_ALLEMAND: _(u'German'),
LANGUE_ANGLAIS: _(u'English'),
LANGUE_ARABE: _(u'Arabic'),
LANGUE_ARMENIEN: _(u'Armenian'),
LANGUE_BENGALI: _(u'Bengali'),
LANGUE_CATALAN: _(u'Catalan'),
LANGUE_CHINOIS: _(u'Chinese'),
LANGUE_COREEN: _(u'Korean'),
LANGUE_CROATE: _(u'Croatian'),
LANGUE_DANOIS: _(u'Danish'),
LANGUE_ESPAGNOL: _(u'Spanish'),
LANGUE_FINNOIS: _(u'Finnish'),
LANGUE_FRANCAIS: _(u'French'),
LANGUE_GREC: _(u'Greek'),
LANGUE_HONGROIS: _(u'Hungarian'),
LANGUE_ITALIEN: _(u'Italian'),
LANGUE_MALAIS: _(u'Malaysian'),
LANGUE_MONGOL: _(u'Mongolian'),
LANGUE_NEERLANDAIS: _(u'Dutch'),
LANGUE_OCCITAN: _(u'Occitan'),
LANGUE_PERSAN: _(u'Persian'),
LANGUE_PORTUGAIS: _(u'Portuguese'),
LANGUE_ROUMAIN: _(u'Romanian'),
LANGUE_RUSSE: _(u'Russian'),
LANGUE_SERBE: _(u'Serbian'),
LANGUE_SLOVAQUE: _(u'Slovakian'),
LANGUE_SLOVENE: _(u'Slovenian'),
LANGUE_SUEDOIS: _(u'Swedish'),
LANGUE_TURC: _(u'Turkish'),
LANGUE_AUTRE: _(u'Other'),
}
When I add it into a form it's like this:
class ProfileForm(forms.ModelForm):
class Meta:
model = Personne
fields = ('blablafields',)
e = {'required': _(u'This field is required'),
'invalid': _(u'This field contains invalid data')}
a = _(u'Mother tongue:')
langue = forms.IntegerField(
label=a, required=True,
widget=forms.Select(attrs={
'title': a,
'groupno': 2,
'class': 'form-control', },
choices=[('', '--')] + [(k, PersonneEnums.TAB_LANGUE[k])
for k in PersonneEnums.TAB_LANGUE]),
error_messages=e)
And the problem is that the form displays the choices "as is". I'd like the choices to be alphabetically sorted.
Is there a way to do this?
You should use OrderedDict
from collections import OrderedDict
class PersonneEnums(object):
LANGUE_ALBANAIS = 0
LANGUE_ALLEMAND = 1
...
TAB_LANGUE = OrderedDict((
(LANGUE_ALBANAIS, _(u'Albanian')),
(LANGUE_ALLEMAND, _(u'German')),
...
))
This way all your items will be ordered the way you put them in TAB_LANGUE.
And then you should use .items()
class ProfileForm(forms.ModelForm):
class Meta:
model = Personne
fields = ('blablafields',)
e = {'required': _(u'This field is required'),
'invalid': _(u'This field contains invalid data')}
a = _(u'Mother tongue:')
langue = forms.IntegerField(
label=a, required=True,
widget=forms.Select(attrs={
'title': a,
'groupno': 2,
'class': 'form-control', },
choices=(('', '--'),) + tuple(PersonneEnums.TAB_LANGUE.items()),
error_messages=e)
Update
Also there is very cool third party django app called dj.choices. It is very helpful for this kind of tasks.
how about this solution:
langues_classees = sorted(PersonneEnums.TAB_LANGUE.items(), key=lambda t: t[1])
.items() does exactly the same as [(k, dictionnaire[k]) for k in dictionnaire]
Also, if you ever need to interface this with any other language management tools at some point, you should use ISO language codes (e.g. ar for arabic, hy for armenian, en for english, zh for chinese, etc.) instead of coming up with your own codes
UPDATE:
The solution above may not be adapted to your use case, because you may want the sorting to happen at the time of the request and depend on the browser's language ('German' comes after 'Arabic' in English but in French, 'Allemand' comes before 'Arabe') rather than when the Form class is declared.
Check a solution for a "Lazy choice field" at https://www.djangosnippets.org/snippets/1767/

Is it possible to change the constants which are defined in django models.py from admin page?

class Parameters(DateTimeBase):
SIMPLE = 1
MEDIUM = 2
VERY_CUT_UP = 3
COMPLEXITY_CHOICES = (
(SIMPLE, 'Simple roof'),
(MEDIUM, 'Medium difficulty'),
(VERY_CUT_UP, 'Very cut-up roof'),
)
complexity = models.IntegerField(choices=COMPLEXITY_CHOICES, default=SIMPLE)

django ratings app , negative scoring

models.py
class Restaurant(models.Model)
food_rating = RatingField(range=2, weight=5,can_change_vote = True,allow_delete = True,allow_anonymous = True)
service_rating = RatingField(range=2, weight=5,can_change_vote = True,allow_delete = True,allow_anonymous = True)
ambience_ratiing = RatingField(range=2, weight=5,can_change_vote = True,allow_delete = True,allow_anonymous = True)
view.py code
r = Restaurant.objects.get(pk=1)
r.food_rating.add(score = -1 , user = request.user , ip_address =request.META.get('HTTP_REFERER'))
print r.food_rating.score
error
djangoratings.exceptions.InvalidRating: -1 is not a valid choice for food_rating
doubt
my food_rating field is eligible to take two scores , how am i supposed to change the score so that i can implement vote up and vote down feature , on vote up , i should be able to add 1 to the existing score and on vote down i should be able to subtract a vote , please help , thanks in advance
The problem comes from this script:
if score < 0 or score > self.field.range:
raise InvalidRating("%s is not a valid choice for %s" % (score, self.field.name))
Short answer: convert the [-x:y] interval you want to use for display, into [-x+x:y+x] in your code to avoid this problem. If you wanted [-5:5], then use [-5+5:5+5] which is [0:10]. If you wanted [-50:100] then use [-50+50:100+50] = [0:150] and so on ... It's a simple formula, that shouldn't be a problem for a programer ;)
Long answer: either you fork djangoratings, either you open an issue asking to add a setting enabling negative ratings ... and probably he'll reject it, because of the simple interval conversion workaround, here's some more concrete examples:
class Restaurant(models.Model):
# blabla :)
ambience_rating = RatingField(range=5, weight=5,can_change_vote = True,allow_delete = True,allow_anonymous = True)
def get_adjusted_ambiance_rating(self):
return self.ambience_rating - 3
So, if ambience_rating is "1" (the lowest score), get_adjusted_ambiance_rating() will return -2.
If ambience_rating is "5" (the highest score), get_ambiance_rating_with_negative() will return 2.
Adapt this example/trick to your needs.
You should probably make a single method for all ratings:
def get_adjusted_rating(self, which):
return getattr(self, '%s_rating' % which) - 3
Callable as such:
restaurant.get_adjusted_rating('ambiance')
restaurant.get_adjusted_rating('food')
# etc ...
And maybe a template filter:
#register.filter
def get_adjusted_rating(restaurant, which):
return restaurant.get_adjusted_rating(which)
Usable as such:
{{ restaurant|get_adjusted_rating:"ambiance" }}
{{ restaurant|get_adjusted_rating:"food" }}
{# etc, etc #}
More details about template filters.