Dynamically creating the related_name from META class - python

Most of my Django models use the same User Mixin, because of this I would like to dynamically create the related_name for the field.
I would like it to be the class name where TestModel becomes test_models or maybe even a set name from the meta class on the main model.
I have looked at self__class__.__name__ but this give me the name of the User class.
Would it be possible to do something like below, if so how....
class User(models.Model):
user = models.ForeignKey(USER, related_name=META.related_name)
class Meta:
abstract = True
class TestModel(User):
title = models.CharField(max_length=80)
class Meta:
related_name = "test_model"

I think it might be sufficient to handle this like it is documented here.
# myapp/models.py
class User(models.Model):
user = models.ForeignKey(
USER,
related_name="%(app_label)s_%(class)s_related"
)
class Meta:
abstract = True
class TestModel(User):
title = models.CharField(max_length=80)
This way the related name would dynamically become myapp_testmodel_related. Of course you can tweak the name and simplify the pattern, if it is certain that the names can't clash between multiple apps.

Related

Filter by child class type

Lets say we have a class Place with a class Restaurant inheriting from it :
from django.db import models
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField(default=False)
serves_pizza = models.BooleanField(default=False)
If I have a class Tag related to places :
class Tag(models.Model):
name = models.CharField(max_length=50)
tagged = models.ManyToManyField(Place, related_name="tags")
For a given tag, how do I get a queryset selecting all Restaurants that have this tag, but not other kind of places ?
The easiest way to doing this is calling filter from Restaurant.objects with something like :
Restaurant.objects.filter(tags=tag)
But if you want call filter from the Place.objects, you must use one of Django polymorphism apps such as Django-Polymorphic in your parent model because Django not supports models polymorphism by default.
Note: Read This article about OOP Polymorphism & This article for some extra information about Django model inheritance.

Django model inheriting from different models, which each inherit from a single class

I am creating a form with Django. This form's ModelForm is built upon multiple models that inherit from base models. The structure of the models is similar to this:
class BaseModel(models.Model):
first_name = models.CharField("First name", max_length=20)
middle_name = models.CharField("Middle name", max_length=20)
last_name = models.CharField("Last Name", max_length=20)
email = models.EmailField("Email address")
phone = models.CharField("Phone number", max_length=16)
Is inherited by
class EmployerModel(BaseModel):
company = models.CharField("Company", max_length=20)
and..
class AdvisorModel(BaseModel):
department = models.CharField("Department", max_length=20)
which is contained in my highest level model (the model that is used in my ModelForm):
class FormModel(EmployerModel, AdvisorModel):
another_field = models.CharField(max_length=20)
and_another_field = models.CharField(max_length=20)
#...
class FormModelForm(forms.ModelForm):
class Meta:
model = FormModel
Can I take this approach while making the form and avoid ORM errors because I have duplicate field names? Is there a way to separate and say; "THESE fields are for a 'Employer'; THESE fields are for an 'Advisor'?"
EDIT
It looks like I need to go with abstract base classes, but I don't know if that fixes the multiple inheritance problem.
Go abstract with parent models, I've successfully written models with this kind of definition:
class Content(ModeratedModel, NullableGenericModel, RatedModel, PicturableModel, PrivacyModel, CommentableModel):
pass
and ModelForms using Content as a model work fine.

If I use Multi Table Inheritance for a set of related classes can they have separate admins?

I need to choose between Multi Table Inheritance and ABC Inheritance and I am not sure if I can still have separate admins for each subclass. I need all the base class fields and all the fields from the subclass in each subclass admin screen.
I am sorry if this is a stupid question, I am still not even finished with the manual but I have a deadline.
Of course, you can have an admin screen for each of your models, and all the fields from base model would be present on child models.
from django docs:
each model in the hierarchy is a model all by itself
Then, if you have:
models.py
class Service(models.Model):
owner = models.ForeignKey('auth.User')
name = models.CharField(max_length=128)
class VariationAService(Service):
# fields
class VariationBService(Service):
# more fields
You can do something like this:
admin.py
class ServiceAdmin(admin.ModelAdmin):
[...]
class VariationAServiceAdmin(admin.ModelAdmin):
[...]
class VariationBServiceAdmin(admin.ModelAdmin):
[...]
You'll get all fields either way. The difference between the two is that with multi-table inheritance...
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
...you'll get both a Place object and Restaurant object, which can both be edited in the admin, but with abstract base classes...
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Meta:
abstract = True
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
...you'll only get a Restaurant object, which can be edited in the admin.

Django Bi-directional ManyToMany - How to prevent table creation on second model?

I have two models, each has a shared ManyToMany, using the db_table field. But how do I prevent syncdb from attempting to create the shared table, for the second model?
class Model1(models.Model):
othermodels = ManyToManyField('Model2', db_table='model1_model2', related_name='model1_model2')
class Model2(models.model):
othermodels = ManyToManyField('Model1', db_table='model1_model2', related_name='model2_model1')
It's working great in my dev environment, because some of the tables got created piecemeal, as I built it all out. But from an empty database, syncdb throws:
_mysql_exceptions.OperationalError: (1050, "Table 'model1_model2' already exists")
Is there a flag that I'm missing from the second model's field to prevent duplicate table creation? Or am I just doing this entirely wrong?
I also found this solution, which worked perfectly for me :
class Test1(models.Model):
tests2 = models.ManyToManyField('Test2', blank=True)
class Test2(models.Model):
tests1 = models.ManyToManyField('Test1', through=Test1.tests2.through, blank=True)
You don't need to put a ManyToManyField on both sides of the relation. Django will do that for you.
You probably want something more like this:
class Model1(models.Model):
name = models.CharField(max_length=128)
...
class Model2(models.Model):
name = models.CharField(max_length=128)
othermodels = models.ManyToManyField(Model1, through='Model1Model2')
...
class Membership(models.Model):
class Meta:
db_table = 'model1_model2'
model1 = models.ForeignKey(Model1)
model2 = models.ForeignKey(Model2)
When you're working with your models, an instance of Model1 will have a othermodels_set field which is automatically added by django. Instances of Model2 will have othermodels.
class Awesome(models.Model):
one = models.TextField()
class Meta:
# Prevent table creation.
abstract = True
http://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes
It's not what you're looking for, but it's the closest they have I belive. Would it not be simpler to make a view?
Maybe:
class Awesome(models.Model):
one = models.CharField(max_length = 255)
two = models.CharField(max_length = 255)
class AwesomeOne(Awesome):
fieldOne = models.ForeignKey(User, related_name = 'one')
class Meta:
abstract = True
class AwesomeTwo(Awesome):
fieldTwo = models.ForeignKey(User, related_name = 'two')
class Meta:
abstract = True
Then, you can have one table created and override the __getattr__ to block access to the original fields.
In Django ManyToMany are bi-directional by default. The think is that you only have to define it on one model, not on both (and usually you don't need to give a name to the table):
class Model1(models.Model):
othermodels = ManyToManyField('Model2')
class Model2(models.model):
...
That's it. Now syncdb will be happy. For more info: Django docs
The only drawback is, if you use the admin, you will have access to othermodels only in Model1.
Edit
So, if you want to have access to the ManyToMany in both models in the Admin, currently the official solution is to use inlinemodel for the second model. I had also this same problem/need just a few days ago. And I was not really satisfied with the inlinemodel solution (heavy in DB queries if you have a lot of entries, cannot use the *filter_horizontal* widget, etc.).
The solution I found (that's working with Django 1.2+ and syncdb) is this:
class Model1(models.Model):
othermodels = models.ManyToManyField('Model2', through='Model1Model2')
class Model2(models.Model):
othermodels = models.ManyToManyField('Model1', through='Model1Model2')
class Model1Model2(models.Model):
model1 = models.ForeignKey(Model1)
model2 = models.ForeignKey(Model2)
class Meta:
db_table = 'app_model1_model2'
auto_created = Model1
See ticket 897 for more info.
Unfortunately, if you're using South you will have to remove the creation of the app_model1_model2 table in every migration file created automatically.

Django Admin: Ordering of ForeignKey and ManyToManyField relations referencing User

I have an application that makes use of Django's UserProfile to extend the built-in Django User model. Looks a bit like:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
# Local Stuff
image_url_s = models.CharField(max_length=128, blank=True)
image_url_m = models.CharField(max_length=128, blank=True)
# Admin
class Admin: pass
I have added a new class to my model:
class Team(models.Model):
name = models.CharField(max_length=128)
manager = models.ForeignKey(User, related_name='manager')
members = models.ManyToManyField(User, blank=True)
And it is registered into the Admin:
class TeamAdmin(admin.ModelAdmin):
list_display = ('name', 'manager')
admin.site.register(Team, TeamAdmin)
Alas, in the admin inteface, when I go to select a manager from the drop-down box, or set team members via the multi-select field, they are ordered by the User numeric ID. For the life of me, I can not figure out how to get these sorted.
I have a similar class with:
class Meta:
ordering = ['name']
That works great! But I don't "own" the User class, and when I try this trick in UserAdmin:
class Meta:
ordering = ['username']
I get:
django.core.management.base.CommandError: One or more models did not validate:
events.userprofile: "ordering" refers to "username", a field that doesn't exist.
user.username doesn't work either. I could specify, like image_url_s if I wanted to . . . how can I tell the admin to sort my lists of users by username? Thanks!
This
class Meta:
ordering = ['username']
should be
ordering = ['user__username']
if it's in your UserProfile admin class. That'll stop the exception, but I don't think it helps you.
Ordering the User model as you describe is quite tricky, but see http://code.djangoproject.com/ticket/6089#comment:8 for a solution.
One way would be to define a custom form to use for your Team model in the admin, and override the manager field to use a queryset with the correct ordering:
from django import forms
class TeamForm(forms.ModelForm):
manager = forms.ModelChoiceField(queryset=User.objects.order_by('username'))
class Meta:
model = Team
class TeamAdmin(admin.ModelAdmin):
list_display = ('name', 'manager')
form = TeamForm
This might be dangerous for some reason, but this can be done in one line in your project's models.py file:
User._meta.ordering=["username"]
For me, the only working solution was to use Proxy Model. As stated in the documentation, you can create own proxy models for even built-in models and customize anything like in regular models:
class OrderedUser(User):
class Meta:
proxy = True
ordering = ["username"]
def __str__(self):
return '%s %s' % (self.first_name, self.last_name)
After that, in your model just change Foreign Key to:
user = models.OneToOneField(OrderedUser, unique=True)
or even more suitable
user = models.OneToOneField(OrderedUser, unique = True, parent_link = True)

Categories