I am in the middle of a development in Django and we got some doubts about if we should use or not constants in the project. Exactly the situation is to use constants everywhere or not (we know constants in Django are not really read-only).
There are 2 scenarios and I would like to get your opinion about which one is better for you and why:
Scenario 1 (using constants)
constants.py
class CONST():
def NAME(): return "name"
def SURNAME(): return "surname"
def ZIPCODE(): return "zipcode"
def CITY(): return "city"
def CREATED(): return "created"
admin.py
from constants import CONST
class RegisterAdmin(admin.ModelAdmin):
list_display = (CONST.NAME(),CONST.SURNAME(),CONS.ZIPCODE())
list_filter = [CONST.ZIPCODE(),CONST.CITY()]
search_fields = [CONST.NAME(), CONST.SURNAME()]
date_hierarchy = CONST.CREATED()
models.py
from constants import CONST
class Register(models.Model):
name = models.CharField(CONST.NAME(), max_length=25)
surname = models.CharField(CONST.SURNAME(), max_length=25)
zipcode = models.IntegerField(CONST.ZIPCODE())
city = models.CharField(CONST.CITY(),max_length=20)
... and any view etc where you use text will be using contants ...
Scenario 2 (without constants)
admin.py
class RegisterAdmin(admin.ModelAdmin):
list_display = ("name","surname","zipcode")
list_filter = ["zipcode","city"]
search_fields = ["name","surname"]
models.py
class Register(models.Model):
name = models.CharField("name", max_length=25)
surname = models.CharField("surname", max_length=25)
zipcode = models.IntegerField("zipcode")
city = models.CharField("city",max_length=20)
I like the most the second scenario (I have been programming python from 2004), for me it looks more efficient, clear and easy to understand. The first scenario (proposed from Java/PHP programmers that now writes Python code) has the advantage that it helps the developer to detect that it made a mistake writing the "constant" so it is easier to detect errors and also it makes easier and quicker "massive changes" on this kind of texts without refactorizing the source code.
I would like to know which source code you would write or use and why.
Thank you,
Scenario 1 is awful. Unfortunately I know all too well the problems of working with Java/PHP developers who are learning python.
Perhaps you can compromise with those guys by proposing the use of python enums to address their concern. These are built-in in python 3.4+, and have been backported as far back as 2.4.
from enum import Enum
class Constant(Enum):
name = "name"
surname = "surname"
zipcode = "zipcode"
city = "city"
created = "created"
Now you can change the "values", say for example changing zipcode to be "potato" in the enum definition, whilst still using the name Constant.zipcode.value everywhere else in source code.
Scenario 1 doesn't make sense. You're using the constants to specify the verbose_name of the fields, and you're using them in the list_display option. The list_display option takes a list of fieldnames and NOT the verbose_name of the fields.
So you don't need constants at all for this. The field names are your "constants".
And IF you would need some "constants" in some case, you shouldn't use methods, just use properties, and just use normal class/attribute name conventions (so no uppercases):
class SomeConstants():
name = "name"
surname = "surname"
zipcode = "zipcode"
city = "city"
created = "created"
The Java/PHP guys might say something like "Yeah but what if one of the constants needs to be generated? Then you need a method!". Well, sure, so you make it a property:
class SomeConstants():
#property
def full_name(self):
return "{} {}".format(self.name, self.surname)
Because of the #property line above the method, it will execute and return the result of this method if you call SomeConstants().full_name.
Use of constants is sometimes good, but not here.
One, the first argument of a field is its verbose name, if it's missing then the name of the field is used by default. So using the same constant for its verbose name as for the field name is exactly the scenario in which the verbose name is completely unnecessary.
Just do
name = CharField(max_length=25)
or
name = CharField("A beautiful description for a name field", max_length=25)
but putting "name" there is completely redundant and thus wrong. Don't repeat yourself.
Second, your constant is actually used for two different things that you don't want to change at the same time: you use the same constant for the attribute name in admin.py, but for verbose name in models.py. They aren't going to stay the same thing.
Third, using constants is only really helpful if the value might change one day, or if it's not immediately obvious what it may mean, for e.g. PI=3.14.
But NAME = "name" does not make anything more obvious, and if it ever changes to NAME = "description" it immediately becomes actively misleading. You'd probably want to change the name of the constant in that case as well, won't you? Why did you ever use a constant then?
And it means that changing something in const.py changes the database structure that Django expects. Some day a developer is going to trip over that; keep field names in models.py.
Finally, at every place where you use the models, you need the actual field anyway. A Register instance is simply a class instance, after all, and you'll probably want to use fields like register.name anywhere. You don't put the names of attributes of other classes in constants, do you?
Just use the string.
What about something more straight-forward like:
constants.py
NAME = "name"
SURNAME = "surname"
admin.py
import constants
class RegisterAdmin(admin.ModelAdmin):
list_display = (constants.NAME, constants.SURNAME,constants.ZIPCODE)
This provides a unique place for the values in contrast to repeating them as in Scenario 2 and thus I would prefer it. It seems more clear than Scenario 1 to me. The "encapsulation" is performed by the module name, i.e. by using constants.xxx, so don't from constants import *.
The values here are obviously not constant, but neither are they in Scenario 1.
Everyone is in agreement that its a bad idea, but it could be that your Java friends are trying to implement the equivalent of a Java resource bundle, perhaps because they fondly remember how easy it is to work with .properties files.
This results in the use of keys in place of actual names and then later on the system will retrieve the correct resource from the bundle. This technique is used for translations and localization of applications.
A typical resource key looks like key.trx.hist.term.transfer.completed.success and at runtime the equivalent resource is rendered by the application.
If this is the case, in Python the approach is a bit different:
You start with a base language. This is what you write your code in.
All strings that need to be localized or translated are marked for translation, with the gettext library. For convenience this library provides a shortcut method _() which is used to mark strings for translation.
These strings are then extracted to a message file; which is forwarded to translators. The message file contains the file names where the string is used. Its a plain text file, much like the .properties file.
This message file is then compiled into an optimized format.
Based on the language/locale of the client, the correct translated strings are displayed.
You can read more on how this is implemented in django at the translation documentation.
Related
I have an idea for data model in django and I was wondering if someone can point out pros and cons for these two setups.
Setup 1: This would be an obvious one. Using CharFields for each field of each object
class Person(models.Model):
name = models.CharField(max_length=255)
surname = models.CharField(max_length=255)
city = models.CharField(max_length=255)
Setup 2: This is the one I am thinking about. Using a ForeignKey to Objects that contain the values that current Object should have.
class Person(models.Model):
name = models.ForeignKey('Name')
surname = models.ForeignKey('Surname')
city = models.ForeignKey('City')
class Chars(models.Model):
value = models.CharField(max_length=255)
def __str__(self):
return self.value
class Meta:
abstract = True
class Name(Chars):pass
class Surname(Chars):pass
class City(Chars):pass
So in setup 1, I would create an Object with:
Person.objects.create(name='Name', surname='Surname', city='City')
and each object would have it's own data. In setup 2, I would have to do this:
_name = Name.objects.get_or_create(value='Name')[0]
_surname = Surname.objects.get_or_create(value='Surname')[0]
_city = City.objects.get_or_create(value='City')[0]
Person.objects.create(name=_name, surname=_surname, city=_city)
Question: Main purpose for this would be to reuse existing values for multiple objects, but is this something worth doing, when you take into consideration that you need multiple hits on the database to create an Object?
Choosing the correct design pattern for your application is a very wide area which is influenced by many factors that are even possibly out of scope in a Stack Overflow question. So in a sense your question could be a bit subjective and too broad.
Nevertheless, I would say that assigning a separate model (class) for first name, another separate for last name etc. is an overkill. You might essentially end up overengineering your app.
The main reasoning behind the above recommendation is that you probably do not want to treat a name as a separate entity and possibly attach additional properties to it. Unless you really would need such a feature, a name is usually a plain string that some users happen to have identical.
It doesn't make any good to keep name and surname as separate object/model/db table. In your setup, if you don't set name and surname as unique, then it doesn't make any sense to put them in separate model. Even worse, it will incur additional DB work and decrease performance. Now, if you set them as unique, then you have to work over the situation when, e.g. some user changes his name and by default it would be changed for all users with that name.
On the other hand, city - there're not that many cities and it's a good idea to keep it as separate object and refer to it via foreign key from user. This will save disk space, allow to easily get all users from same city. Even better, you can prepopulate cities DB and provide autocompletion fro users entering there city. Though for performance you might still want to keep city as a string on the user model.
Also, to mention 'gender' field, since there're not many possible choices for this data, it's worth to use enumeration in your code and store a value in DB, i.e. use choices instead of ForeignKey to a separate DB table.
I have my models.py :
class Hotel(models.Model):
name = models.CharField(max_length=20)
currency = models.ForeignKey(Currency)
class Currency(models.Mode):
code = models.CharField(max_length=3)
name = models.CharField(max_length=10)
Whenever the currency field in hotel is changing I need to be able to do something. So I have a function like :
#receiver(pre_save,sender=Hotel)
def update_something(sender,**kwargs)
obj = kwargs['instance']
old_object = Hotel.objects.get(pk=obj.pk)
'''
Now I can do anything here comparing the oldo object with the instance
'''
The thing is I don't want to make a query for this, since then the purpose of signals becomes stupid and I become a fool.
So I should be able to do something like :
updated = kwargs['update_fields']
new_currency = updated['currency']
Is their a way I can find out that change for ONLY one particular field say currency ,instead of doing a query like this. I want to get the changes related to the currency foreign key and update things before I save.
Sorry for my bad English and not being able to use too technical terms.
Thanks :)
Rather hacky solution is to save object state on initialization.
from django.forms.models import model_to_dict
class Currency(models.Mode):
code = models.CharField(max_length=3)
name = models.CharField(max_length=10)
def __init__(self):
super(Currency, self).__init__()
self.__state = model_to_dict(self)
def updated(self):
new_state = model_to_dict(self)
return dict(set(self.__state.iteritems()) - set(new_state.iteritems()))
Method updated will return difference between initial and new states.
The point of signals is better decoupling - allow other applications (that you don't necessarily know yet) to transparently hook into your application and/or avoid circular dependencies between application. There's really no point in using a signal here afaik (unless of course the signal handler is in another app and you don't want the app with the Hotel and Currency models to depend on this other app).
To answer your question: doing a query to retrieve the original state is the only way you can compare stored value and current value. Now if you're only interested in one specific field, you of course don't have to retrieve the whole model instance - just use queryset.values_list:
old_currency = Hotel.objects.filter(pk=obj.pk).values_list("currency", flat=True)[0]
I have a set of DB tables which store customer order counts per minute per day. Each month of the year is a different table in order to avoid excessively large tables. In order to retrieve this data in my Django webpage, I dynamically create a model class with db_table being populated based on the date received from an html form input. The problem is that when I resubmit the form with a new date value, the class does not update to what should be the new model, it still maintains the old value.
My models.py looks something like this:
class baseModel(models.Model):
id = models.CharField(max_length=40)
date = models.IntegerField()
minute = models.IntegerField()
totalorders = models.IntegerField()
class Meta:
abstract = True
managed = False
def getModel(type, yyyymm):
if type == 'duration':
class DurationClass(baseModel):
medianduration = models.IntegerField()
avgduration = models.IntegerField()
class Meta:
db_table='orderTable' + yyyymm
#debug statement
print db_table
return DurationClass
yyyymm is just a string, like '201204' for April 2012. So if I enter April 2012 into the input box it works fine but then if I change to March 2012, it still queries for April data. I can see from the debug statement that db_table is being properly updated, but for some reason it's not working. Do I need to deallocate the old dynamic model before allocating a new one or something? In view.py, I'm just doing this (not in a loop):
myModel = getModel('duration', startyyyymm)
QS = myModel.objects.using( ...etc
Many thanks for any ideas.
You have a problem about how python manage the creation of dynamic clases. I don't know exactly how python works, but it seems to be that the way you do it is not totally correct. I think it is because python classes are attached to one module, so the first time you execute "getModel" it creates the model as you expect. But, after that, every time you execute "getModel", as the class has always the same name, python can't create the same class at the same module, so it somehow returns you the same class you create the first time you call "getModel". (I hope you understand my English, although i might be wrong about how python dynamic classes creation works)
I search a little and make some tests before giving you an answer. It seems to be that the best way of creating a dynamic class is using "type" (python built-in method), so you can create one class per table (this classes must have a different name).
Here's an example of what you can do (it worked for me):
def getModel(type, yyyymm):
if type == 'duration':
newModelClass = type(
'newModelName', #It could be the table name you are going to search in. It must be different for every different db table you want to use. For example: 'orderTable' + yyyymm
(baseModel, ), #Base class for your new model
{
'medianduration' : models.IntegerField(), #New model's attribute
'avgduration' : models.IntegerField(), #New model's attribute
'__module__':__name__, #This is required. If not given, type raises a KeyError
'Meta': type(
'Meta',
(object,),
{
'db_table':'orderTable' + yyyymm, #Here you put the table name you want to use
'__module__':__name__,
}
)
}
)
return newModelClass
If i didn't make any copy/paste mistake, it should now work for you.
Again, sorry if i make any English mistake. I'm a little bit rusty at writing in English and in English in general :S
I hope it helps you. Althought i agree that your database should work fine without using multiple tables...
All you need is given the below link: https://code.djangoproject.com/wiki/DynamicModels
I'm currently doing a firewall management application for Django, here's the (simplified) model :
class Port(models.Model):
number = models.PositiveIntegerField(primary_key=True)
application = models.CharField(max_length=16, blank=True)
class Rule(models.Model):
port = models.ForeignKey(Port)
ip_source = models.IPAddressField()
ip_mask = models.IntegerField(validators=[MaxValueValidator(32)])
machine = models.ForeignKey("vmm.machine")
What I would like to do, however, is to display to the user a form for entering rules, but with a very different organization than the model :
Port 80
O Not open
O Everywhere
O Specific addresses :
--------- delete field
--------- delete field
+ add address field
Port 443
... etc
Where Not open means that there is no rule for the given port, Everywhere means that there is only ONE rule (0.0.0.0/0) for the given port, and with specific addresses, you can add as many addresses as you want (I did this with JQuery), which will make as many rules.
Now I did a version completely "handmade", meaning that I create the forms entirely in my templates, set input names with a prefix, and parse all the POSTed stuff in my view (which is quite painful, and means that there's no point in using a web framework).
I also have a class which aggregates the rules together to easily pre-fill the forms with the informations "not open, everywhere, ...". I'm passing a list of those to the template, therefore it acts as an interface between my model and my "handmade" form :
class MachinePort(object):
def __init__(self, machine, port):
self.machine = machine
self.port = port
#property
def fully_open(self):
for rule in self.port.rule_set.filter(machine=self.machine):
if ipaddr.IPv4Network("%s/%s" % (rule.ip_source, rule.ip_mask)) == ipaddr.IPv4Network("0.0.0.0/0"):
return True
else :
return False
#property
def partly_open(self):
return bool(self.port.rule_set.filter(machine=self.machine)) and not self.fully_open
#property
def not_open(self):
return not self.partly_open and not self.fully_open
But all this is rather ugly ! Do anyone of you know if there is a classy way to do this ? In particular with the form... I don't know how to have a form that can have an undefined number of fields, neither how to transform these fields into Rule objects (because all the rule fields would have to be gathered from the form), neither how to save multiple objects... Well I could try to hack into the Form class, but seems like too much work for such a special case. Is there any nice feature I'm missing ?
You can create usual Forms objects by subclassing Form and adding fields in constructor, as in:
self.base_fields[field_name] = field_instance
As for the Rule, You can create a custom Field that will validate() itself according to Your rules and add it to Your custom form as above.
So Yes, it must be handmande (AFAIK), but it's not so much code.
Ok, finally I got it running by making the models closer to what I wanted to present to the user. But related to the topic of the question :
1) Nested forms/formsets are not a built-in Django feature, are a pain to implement by yourself, and are actually not needed... Rather, one should use forms' and formsets' prefixes.
2) Trying to work with forms not based on the models, process the data, then reinject it in the models, is much much more code than modifying the models a little bit to have nice model-based forms.
So what I did is I modified the models like that :
class PortConfig(Serializable):
port = models.ForeignKey(Port, editable=False)
machine = models.ForeignKey("vmm.machine", editable=False)
is_open = models.CharField(max_length=16, default="not_open", choices=is_open_choices)
class Rule(Serializable):
ip_source = models.CharField(max_length=24)
port_config = models.ForeignKey(PortConfig)
Then I simply used a "model formset" for PortConfig, and "model inline formset" for Rule, with a PortConfig as foreign key, and it went perfectly
3) I used this great JS library http://code.google.com/p/django-dynamic-formset/ to put the "add field" and "delete field" links ... you almost have nothing to do.
Lets say I have the following Django model:
class StandardLabel(models.Model):
id = models.AutoField(primary_key=True)
label = models.CharField(max_length=255)
abbreviation = models.CharField(max_length=255)
Each label has an ID number, the label text, and an abbreviation. Now, I want to have these labels translatable into other languages. What is the best way to do this?
As I see it, I have a few options:
1: Add the translations as fields on the model:
class StandardLabel(models.Model):
id = models.AutoField(primary_key=True)
label_english = models.CharField(max_length=255)
abbreviation_english = models.CharField(max_length=255)
label_spanish = models.CharField(max_length=255)
abbreviation_spanish = models.CharField(max_length=255)
This is obviously not ideal - adding languages requires editing the model, the correct field name depends on the language.
2: Add the language as a foreign key:
class StandardLabel(models.Model):
id = models.AutoField(primary_key=True)
label = models.CharField(max_length=255)
abbreviation = models.CharField(max_length=255)
language = models.ForeignKey('languages.Language')
This is much better, now I can ask for all labels with a certain language, and throw them into a dict:
labels = StandardLabel.objects.filter(language=1)
labels = dict((x.pk, x) for x in labels)
But the problem here is that the labels dict is meant to be a lookup table, like so:
x = OtherObjectWithAReferenceToTheseLabels.object.get(pk=3)
thelabel = labels[x.labelIdNumber].label
Which doesn't work if there is a row per label, possibly with multiple languages for a single label. To solve that one, I need another field:
class StandardLabel(models.Model):
id = models.AutoField(primary_key=True)
group_id = models.IntegerField(db_index=True)
label = models.CharField(max_length=255)
abbreviation = models.CharField(max_length=255)
language = models.ForeignKey('languages.Language')
class Meta:
unique_together=(("group_id", "language"),)
#and I need to group them differently:
labels = StandardLabel.objects.filter(language=1)
labels = dict((x.group_id, x) for x in labels)
3: Throw label text out into a new model:
class StandardLabel(models.Model):
id = models.AutoField(primary_key=True)
text = models.ManyToManyField('LabelText')
class LabelText(models.Model):
id = models.AutoField(primary_key=True)
label = models.CharField(max_length=255)
abbreviation = models.CharField(max_length=255)
language = models.ForeignKey('languages.Language')
labels = StandardLabel.objects.filter(text__language=1)
labels = dict((x.pk, x) for x in labels)
But then this doesn't work, and causes a database hit every time I reference the label's text:
x = OtherObjectWithAReferenceToTheseLabels.object.get(pk=3)
thelabel = labels[x.labelIdNumber].text.get(language=1)
I've implemented option 2, but I find it very ugly - i don't like the group_id field, and I can't think of anything better to name it. In addition, StandardLabel as i'm using it is an abstract model, which I subclass to get different label sets for different fields.
I suppose that if option 3 /didn't/ hit the database, it's what I'd choose. I believe the real problem is that the filter text__language=1 doesn't cache the LabelText instances, and so the DB is hit when I text.get(language=1)
What are your thoughts on this? Can anyone recommend a cleaner solution?
Edit: Just to make it clear, these are not form labels, so the Django Internationalization system doesn't help.
Another option you might consider, depending on your application design of course, is to make use of Django's internationalization features. The approach they use is quite common to the approach found in desktop software.
I see the question was edited to add a reference to Django internationalization, so you do know about it, but the intl features in Django apply to much more than just Forms; it touchs quite a lot, and needs only a few tweaks to your app design.
Their docs are here: http://docs.djangoproject.com/en/dev/topics/i18n/#topics-i18n
The idea is that you define your model as if there was only one language. In other words, make no reference to language at all, and put only, say, English in the model.
So:
class StandardLabel(models.Model):
abbreviation = models.CharField(max_length=255)
label = models.CharField(max_length=255)
I know this looks like you've totally thrown out the language issue, but you've actually just relocated it. Instead of the language being in your data model, you've pushed it to the view.
The django internationalization features allow you to generate text translation files, and provides a number of features for pulling text out of the system into files. This is actually quite useful because it allows you to send plain files to your translator, which makes their job easier. Adding a new language is as easy as getting the file translated into a new language.
The translation files define the label from the database, and a translation for that language. There are functions for handling the language translation dynamically at run time for models, admin views, javascript, and templates.
For example, in a template, you might do something like:
<b>Hello {% trans "Here's the string in english" %}</b>
Or in view code, you could do:
# See docs on setting language, or getting Django to auto-set language
s = StandardLabel.objects.get(id=1)
lang_specific_label = ugettext(s.label)
Of course, if your app is all about entering new languages on the fly, then this approach may not work for you. Still, have a look at the Internationalization project as you may either be able to use it "as is", or be inspired to a django-appropriate solution that does work for your domain.
I would keep things as simple as possible. The lookup will be faster and the code cleaner with something like this:
class StandardLabel(models.Model):
abbreviation = models.CharField(max_length=255)
label = models.CharField(max_length=255)
language = models.CharField(max_length=2)
# or, alternately, specify language as a foreign key:
#language = models.ForeignKey(Language)
class Meta:
unique_together = ('language', 'abbreviation')
Then query based on abbreviation and language:
l = StandardLabel.objects.get(language='en', abbreviation='suite')
I'd much prefer to add a field per language than a new model instance per language. It does require schema alteration when you add a new language, but that isn't hard, and how often do you expect to add languages? In the meantime, it'll give you better database performance (no added joins or indexes) and you don't have to muck up your query logic with translation stuff; keep it all in the templates where it belongs.
Even better, use a reusable app like django-transmeta or django-modeltranslation that makes this stupid simple and almost completely transparent.
Although I would go with Daniel's solution, here is an alternative from what I've understood from your comments:
You can use an XMLField or JSONField to store your language/translation pairs. This would allow your objects referencing your labels to use a single id for all translations. And then you can have a custom manager method to call a specific translation:
Label.objects.get_by_language('ru', **kwargs)
Or a slightly cleaner and slightly more complicated solution that plays well with admin would be to denormalize the XMLField to another model with many-to-one relationship to the Label model. Same API, but instead of parsing XML it could query related models.
For both suggestions there's a single object where users of a label will point to.
I wouldn't worry about the queries too much, Django caches queries and your DBMS would probably have superior caching there as well.