I'm working on my first real Django project after years of PHP programming, and I am running into a problem with my models. First, I noticed that I was copying and pasting code between the models, and being a diligent OO programmer I decided to make a parent class that the other models could inherit from:
class Common(model.Model):
name = models.CharField(max_length=255)
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
def __unicode__(self):
return self.name
class Meta:
abstract=True
So far so good. Now all my other models extend "Common" and have names and dates like I want. However, I have a class for "Categories" were the name has to be unique. I assume there should be a relatively simple way for me to access the name attribute from Common and make it unique. However, the different methods I have tried to use have all failed. For example:
class Category(Common):
def __init__(self, *args, **kwargs):
self.name.unique=True
Causes the Django admin page to spit up the error "Caught an exception while rendering: 'Category' object has no attribute 'name'
Can someone point me in the right direction?
No, Django doesn't allow that.
See the docs: http://docs.djangoproject.com/en/1.1/topics/db/models/#field-name-hiding-is-not-permitted
Also answered in other questions like: In Django - Model Inheritance - Does it allow you to override a parent model's attribute?
You have a small mistake in your Common class
class Common(model.Model):
self.name = models.CharField(max_length=255)
should be
class Common(model.Model):
name = models.CharField(max_length=255)
Note that UNIQUE constraint in fact has nothing to do with Django, so you're free to add it in your database table. You can also use post-syncdb hook for that purpose.
Try using Meta.unique_together to force it into its own unique index. Failing that, it's probably easiest to create two separate abstract classes, one with the field unique and one not.
Related
I have written a base class for admin classes for all of my classes that have a field called is_active. It enables the user to see the records of the table along with the is_active field so that they can see which record is active or not and they can edit it.
For most of the classes that I have in my models, there is a field called name so I can easily write 2 lines of the code in admin.py and have a customized admin representation, but the problem is for the classes that don't have a field called name so I have to use a different field in list_display . Since the number of these classes is high, I am looking for a solution to either dynamically get the name of the field that must be in the list_display. Any ideas to solve this problem would be much appreciated.
models.py:
class BaseAdmin(admin.ModelAdmin):
list_display=('id','name','is_active')
list_editable = ('is_active',) # this MUST only contain fields that also are in "list_display"
search_fields=('name',)
class ClassA(models.Model):
name=models.CharField(max_length=20, blank=False, unique=True,)
is_active=models.BooleanField(default=True,)
def __str__(self):
return self.name
class ClassB(models.Model):
my_field=models.CharField(max_length=20, blank=False, unique=True,)
is_active=models.BooleanField(default=True,)
def __str__(self):
return self.my_field
admin.py
class ClassAAdmin(BaseAdmin):
pass
class ClassBAdmin(BaseAdmin):
pass
You are looking for get_list_display https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.get_list_display
Use that in conjunction with ModelAdmin.model (i.e. self.model inside the get_list_display method) to create a dynamic list display based on the model.
Note that I would personally recommend not baking this automation into the BaseAdmin, and defining what is different between the subclass ModelAdmins (in this case, list_display) separately. It's more explicit and clear. This is pure opinion though.
I have been trying to figure out the best way to automate creating multiple SQL tables based on separate but identical models, all based on the same base class. I'm basically creating pseudo message boards or walls with different Groups, and I wanted each Group to have its own db_table of Posts, each Post containing the user id, timestamp, etc.
My first thought was to have one base class of Posts and just include a field for Group name, but I thought this would be bad practice. My rationale was that one table containing every Post for all Groups would get really big (in theory anyway) and slow down filtering, and also that the extra field for group name would in the long run be a waste of memory when I could have separate tables per group and skip this field.
I've also considered using a ForeignKey with a Many-to-One relationship, but as far as I can tell this has the same drawbacks. Am I wrong to think that? Or are these size concerns not really an issue?
So my next idea was to make Posts an abstract class, and then create subclasses based on each Group. This is ultimately what I did. However, I found myself having to copy and paste the code over and over and change the class name each time. This felt very unPythonic to me. It was something like:
class Posts(models.Model):
timestamp = models.DateTimeField(auto_now_add=True, unique=False)
user_id = ...
#etc.
#
class Meta:
abstract = True
class GroupA(Posts):
class Meta(Posts.Meta):
db_table = 'groupa_board'
class GroupB(Posts):
class Meta(Posts.Meta):
db_table = 'groupb_board'
class GroupC...etc.
What I really was looking for was a factory function to do this for me. I tried this sort of thing:
def makeBoard(group):
class Board(Posts):
class Meta(Posts.Meta):
db_table = group
return board #note I tried with and without this line
And then I ran a simple for loop using a list of groups.
for group in groups:
makeBoard(group)
I found myself hitting a RuntimeError: conflicting models in application, and I probably deserved it. So then I figured what I need is something like:
def makeBoard(group):
class group(Posts): #***group here being a variable, not the class name
class Meta(Posts.Meta):
db_table = '%s' % group #maybe issues here too, but the table
return group #name is not that important if the class
#name works
But I couldn't figure out how to make this work! Is there a way to pass a variable from a list to a class name?
Anyway if you're still with me I appreciate it. I've been on stackoverflow all day and while I've found guides for creating abstract base classes and subclasses to solve similar issues, I didn't see a way to create a function to do this for me. I ultimately punted here and just make a subclass for each group by hand. If there is a way to automate this process, I'd love to hear it.
Also, if I'm being stupid for not just going with one db table containing every post, I'd like to know that too, and why! Or if there's a better way to implement this kind of system altogether. I apologize if this has been answered before, I really couldn't find it.
Thank you!
Using a single table would not be bad practice. The extra memory is minimal, on modern systems that shouldn't be a problem. You shouldn't worry about performance either, premature optimization (not including the actual system design) is considered bad practice, but if you run into performance problems you can always specify an index on the group column:
group = models.CharField(max_length=100, db_index=True)
That's not to say that it is the best option, or that your method isn't good. Also, it is entirely possible to dynamically create models, using the type() built-in function. The only difference with dynamically creating models and creating other classes is that you must specifically pass the __module__ attribute. You can create subclasses for Posts in the following way:
def fabric(names, baseclass=Posts):
for name in names:
class Meta:
db_table = '%s_table' % name.lower()
attrs = {'__module__': baseclass.__module__, 'Meta': Meta}
# specify any other class attributes here. E.g. you can specify extra fields:
attrs.update({'my_field': models.CharField(max_length=100)})
newclass = type(str(name), (baseclass,), attrs)
globals()[name] = newclass
fabric(['GroupA', 'GroupB', 'GroupC', etc...])
Put that code in your models.py after your Posts class, and all classes will be created for you. They can be used in any way normal classes can be used: Django doesn't even know you dynamically created this class. Though your Meta class doesn't inherit from Posts.Meta, your meta settings should still be preserved.
Tested with Django 1.4.
Try smth like this
import app.models as group_models
from django.db.models.base import ModelBase
def fabric(group):
for item in dir(group_models):
c = getattr(group_models, item)
if type(c) is ModelBase:
if c._meta.db_table == '%s_table' % group:
return c
return None
I'm currently experimenting with model mixins, the idea being to build a small library of small abstract mixin classes defining commonly needed fields.
Here's what i'm currently playing with:
class HtmlAttrsMixin(models.Model):
css_classes = models.CharField(
max_length=256,
verbose_name=_('CSS Classes'),
blank=True,
)
class Meta:
abstract = True
class LinkHtmlAttrsMixin(HtmlAttrsMixin):
target_blank = models.BooleanField(
default=False,
verbose_name=_('Open in a new window /tab'),
)
title = models.CharField(
max_length=512,
verbose_name=_('Title'),
blank=True,
)
class Meta:
abstract = True
class URLMixin(models.Model):
label = models.CharField(
max_length=256,
verbose_name=_('Name'),
blank=True,
)
url = models.CharField(
max_length=4000,
verbose_name=_('URL'),
blank=True,
)
class Meta:
abstract = True
# Concrete model implementing the above mixins:
class TagLine(URLMixin, LinkHtmlAttrsMixin):
enabled = models.BooleanField(_("enabled"), default=True)
def __unicode__(self):
return self.label
This is working fine so far, but there's a little something i don't quite understand.
I'd like to be able to kind of decide of the inherited fields' ordering by simply declaring the mixins in a different order. As far as i know, the default field ordering is based on the order in which those were declared on the model class, and python will resolve attribute names by going through the base classes in the order in which they were listed, so in the above example, i'd expect the css_classes, target_blank & title fields to be listed AFTER label & url in the admin site, and vice versa if i invert the order of the two mixins in the declaration.
But no matter how i list the mixins, the "html_attrs" fields keep appearing first. They are grouped together (which seems logical, since they belong to the same class), but i just can't seem to force the ordering in that way.
I know this is a trivial question - I can just fix that with an admin fieldset, which will prove much more flexible anyway. It just seemed like a convenient trick which i expected to work, so i'm simply interested in understanding why it doesn't.
(Also, if anyone has any advice about model mixins in general, i'm all ears - I've found some info, but not much, and google for django models mixins tends to return lots of results about CBV mixins, which is not what i'm interested in at the moment).
Thanks to anyone who'll care to answer!
This doesn't answer your question, but I do think the approach is really nice.
Reordering fields in Django model
For forms:
How does Django Know the Order to Render Form Fields?
Instead of using a fields attribute in your form to be edited every time you add a new field to your Model class, make a logic to add the exception where you want it.
Another approach: you can use from django.forms import fields_for_model, and create a function to populate your fields attribute using this function. Check the documentation for this method, is really nice!
I'm using Satchmo and Django and am trying to extend Satchmo's Product model. I'd like to make one of the fields in Satchmo's Product model have a default value in the admin without changing Satchmo's source code. Here is an abbreviated version of Satchmo's Product model:
class Product(models.Model):
site = models.ForeignKey(Site, verbose_name='Site')
This is what I attempted to do to extend it...
class MyProduct(Product):
Product.site = models.ForeignKey(Site, verbose_name='Site', editable=False, default=1)
This does not work, any ideas on why?
For two reasons, firstly the way you are trying to override a class variable just isn't how it works in Python. You just define it in the class as normal, the same way that def __init__(self): is overriding the super-class initializer. But, Django model inheritance simply doesn't support this. If you want to add constraints, you could do so in the save() method.
You could probably monkeypatch it if you really wanted to:
site_field = Product._meta.get_field('site')
site_field.editable = False
site_field.default = 1
But this is a nasty habit and could cause problems; arguably less maintainable than just patching Satchmo's source directly.
You can't change the superclass from a subclass.
You have the source. Use subversion. Make the change. When Satchmo is updated merge the updates around your change.
First of all,I'm not into web programming. I bumped into django and read a bit about models. I was intrigued by the following code ( from djangoproject.com ) :
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
def __str__(self):
# Note use of django.utils.encoding.smart_str() here because
# first_name and last_name will be unicode strings.
return smart_str('%s %s' % (self.first_name, self.last_name))
By my understanding of python , first_name and last_name are class variables , right ? How is that used in code ( because I guess that setting Person.first_name or Person.last_name will affect all Person instances ) ? Why is it used that way ?
The essence of your question is "how come these class variables (which I assign Field objects to) suddenly become instance variables (which I assign data to) in Django's ORM"? The answer to that is the magic of Python metaclasses.
A metaclass allows you to hook into and modify the process of creating a Python class (not the creation of an instance of that class, the creation of the class itself).
Django's Model object (and thus also your models, which are subclasses) has a ModelBase metaclass. It looks through all the class attributes of your model, and any that are instances of a Field subclass it moves into a fields list. That list is assigned as an attribute of the _meta object, which is a class attribute of the model. Thus you can always get to the actual Field objects via MyModel._meta.fields, or MyModel._meta.get_field('field_name').
The Model.__init__ method is then able to use the _meta.fields list to determine what instance attributes should be initialized when a model instance is created.
Don't be afraid to dive into the Django source code; it's a great source of education!
Yes, first_name and last_name are class variables. They define fields that will be created in a database table. There is a Person table that has first_name and last_name columns, so it makes sense for them to be at Class level at this point.
For more on models, see:
http://docs.djangoproject.com/en/dev/topics/db/models/
When it comes to accessing instances of a Person in code, you are typically doing this via Django's ORM, and at this point they essentially behave as instance variables.
For more on model instances, see:
http://docs.djangoproject.com/en/dev/ref/models/instances/?from=olddocs
Not a real answer, but for enrichment:
Person.first_name
won't work
p = Person.objects.get(pk=x)
p.first_name
will work. so an object instance of person has a first and last name, but static context Person does not.
Also note: Django has Model Managers which are allow "Person" to do static queryset operations. (https://docs.djangoproject.com/en/dev/topics/db/managers/#managers).
so for example
peoples = Person.objects.all()