Access and manipulate The Value of class Meta attributes - django

Is it possible to change the value of an attribute of a class Meta: for example I have a model like:-
class Mymodel(models.Model):
class Meta:
ordering = ['-some_order']
for example I want to change the value of display to some value using a view. How do I access the class Meta attributes so that I can manupulate the values?

You can't change them. They're processed by the metaclass when the model is first defined: they're not changeable. Although I can't imagine why you'd want to.
You can read them via Mymodel._meta, if you really want.

Related

How to reference model joined across foreign key in django admin

I have a django 1.6 app with the following (trimmed for clarity)
classes defined. User is the standard django.contrib.auth User class.
class Event(models.Model):
user = models.ForeignKey(User, related_name='events')
name = models.CharField(max_length=64)
class Profile(models.Model):
user = models.ForeignKey(User, related_name='aprofile')
class MemberProfile(Profile):
pass
Here are my admin classes:
class ProfileAdmin(ModelAdmin):
model = Profile
fields = ('user', )
class MemberProfileAdmin(ModelAdmin):
model = MemberProfile
fields = ('user', )
readonly_fields = ('user', )
What I'd like to do is display a read-only list of all events for a given member, or at least profile. Of course joining across the User foreign key seems like the way to go, but I am drawing a blank as to how to accomplish this. Here's a summary of attempts so far.
Define an inline admin on the Event class directly referencing the user field, and add it to the ProfileAdmin:
class EventInlineAdmin(TabularInline):
model = Event
fk_name = 'user' # Fails - fk_name 'user' is not a ForeignKey to <class 'solo365.solo_profile.models.profile.Profile'>
...well, no, it sure isn't. But our User has an 'aprofile' field, so...
class EventInlineAdmin(TabularInline):
model = Event
fk_name = 'user__aprofile' # Fails - EventInlineAdmin.fk_name' refers to field 'user__aprofile' that is missing from model 'admin_fk_test.Event'.
Ok, those fields look like they should sync up, but perhaps we need to be a little more aggressive:
class EventInlineAdmin(TabularInline):
model = Event
fk_name = 'user__aprofile__pk' # Fails - 'EventInlineAdmin.fk_name' refers to field 'user__aprofile__pk' that is missing from model 'admin_fk_test.Event'.
I've also tried messing with formfield_for_foreignkey() and friends in both the inline and the regular model admins, but without that fk_name having a valid value, those methods never get called.
I then considered trying to access the events field directly from a Profile's user:
class ProfileAdmin(ModelAdmin):
model = Profile
fields = ('user', 'user__events') # Fails - Unknown field(s) (user__events) specified for Profile. Check fields/fieldsets/exclude attributes of class ProfileAdmin.
What about with a custom formfield_for_foreignkey() method? Sadly that never gets called for anything other than the 'user' field. I've also considered a custom get_formsets() method, but frankly I'm not sure how I could use that without a working EventInlineAdmin.
I could of course define a custom field that simply concatenates all of the events and returns that as a string, but ideally I would prefer something like a fully-featured inline (even read-only) than just a chunk o' text. IOW such a custom field would have a method that (ideally) would return an inline form without requiring any sort of custom template, setting of allow_tags, etc.
Am I doomed to have to create a completely custom Form for the Profile admin class? Or is there a simple way to accomplish what I'm trying to do, that I'm just missing?
Update:
Bonus points if a provided solution works for the MemberProfileAdmin class, not just the ProfileAdmin class.
The relation between User and Profile should be a 1:1 relation which would allow the referencing via user__aprofile. Otherwise, the reverse relation of a foreing key is a queryset because one foreign key can be assigned to multiple instances. This is might be the reason why your code failed.
Change it to:
class Profile(models.Model):
user = models.OneToOneKey(User, related_name='aprofile')
This is a bit like using ForeignKey(unique=True).
To know the attributes, it might help to call dir(model_instance) on the model instance in question, or try around in the Django shell (./manage.py shell).
Also, I've experienced that it might be more confusing to assign a custom related_name like in your case where you would expect one profile by looking at the related name but you would actually get back a queryset.
The generated name in that case would be profile_set, and you would have to call profile_set.all() or profile_set.values() to get some actual profiles.

Using model inheritance, how could you order two different models differently in Django?

I have a model named Photo and a model named Video. They both inherit off a model named Item, which contains the a time_created field and item_by field. I know that you can order objects from these two different models by using Item.objects.all().order_by('time_created'). However, I'd like to order Photo by time and Video by rank. I would still like to group these two objects under one queryset, even if they are ordered differently, so that I could display it on one page (like a grid). How would I be able to do this?
Thanks!
I know you can accomplish something similar by overriding the child models Meta ordering property. However without seeing your model code (proxy, abstract etc.) I can't say for certain if this will work for you.
class Item(models.Model):
time_created = models.DateField()
rank = models.IntegerField()
class Photo(Item):
class Meta:
ordering = ['time_created']
class Video(Item):
class Meta:
ordering = ['rank']

FeinCMS, intermediate model on content type field

I'm trying to accomplish the following:
class DownloadContentFiles(models.Model):
download_content = models.ForeignKey('DownloadContent')
media_file = models.ForeignKey(MediaFile)
class DownloadContent(models.Model):
files = models.ManyToManyField(MediaFile, through=DownloadContentFiles)
class Meta:
abstract=True
I can see why this doesn't work. Because of the abstract on DownloadContent.
Is there a workaround to specify a intermediate model for contenttype fields?
Generally, if you need more informations when creating a field (such as a list of choices) or a concrete Django model (as you do), you can use initialize_type for that.
class DownloadContent(models.Model):
#classmethod
def initialize_type(cls):
cls.add_to_class('files', ... your model field ...)
The MediaFileContent uses this method to add the type selector:
https://github.com/feincms/feincms/blob/master/feincms/content/medialibrary/models.py#L58
However, in your case this does not work because you'd also have to create the through model dynamically. The reason for that is that for each concrete DownloadContent, you'd need another concrete DownloadContentFiles model.
You could achieve this by using the type built-in to dynamically create new DownloadContentFiles concrete classes (beware of name clashes when using DownloadContent with different CMS bases such as page.Page and elephantblog.Entry).
Maybe a simpler way to achieve what you want:
Add a Downloads model somewhere, and add the files ManyToManyField to this class instead
The DownloadContent only contains a ForeignKey(Downloads)
Yes, you need another model. It might be worth it because you can build a better editing interface for Downloads, and the page editor is also simplified because you only have to select one of the already existing Downloads models for showing them on a page.
Maybe explicitely defining class_name to create_content_type could work for you. Something like this:
class DownloadContentFiles(models.Model):
download_content = models.ForeignKey('MyDownloadContent')
media_file = models.ForeignKey(MediaFile)
class DownloadContent(models.Model):
files = models.ManyToManyField(MediaFile, through=DownloadContentFiles)
class Meta:
abstract=True
Page.create_content_type(DownloadContent, class_name="MyDownloadContent")

Dynamically add to the model's Meta class?

Normally, you specify a table name for your model by putting in the model class:
class MyModel(models.Model):
class Meta:
db_table = 'desired_tablename'
I want to add the table names for all my models dynamically at the end of models.py.
Can this be done in Django? If yes, how?
Yes, this can be done simply like this:
MyModel._meta.db_table = 'desired_tablename'

How can I get the ForeignKey field instead of the related object in Django?

I have this:
Class A(models.Model):
name = models.CharField(max_length=50)
Class B(models.Model):
a = models.ForeignKey(A)
class C(models.Model):
a = models.ManyToManyField(A)
When i need the attribute a in an object of C:
related_manager = getattr(object_c,'a')
and this give me a ManyRelatedManager but the problem is when i need the attribute a in a object of B:
object_b2 = getattr(object_b,'a')
this give me a object of class B and i need know if is a ForeignKey or ManyToManyField, i mean, i need getattr(something, 'some_attribute') and get the models.* not the object in self.
I've run into this before with getattr. Model introspection is the solution.
If you know the field name, the best way to do it is to use a method in the object's _meta section (ooooh, scary!).
object_b2._meta.get_field_by_name('a')[0]
That [0] at the end is because the method actually returns lots of other useful and interesting information in a tuple. But if you just want the field itself, that's the way to do it.
Without giving code, the way to do it if you don't know the name of the field is to iterate of _meta.fields and find the one where field.rel.to is the model you're looking for.
Good luck!
A ForeignKey only leads to a single model. If you need to get a manager when accessing the attribute then use ManyToMany with an intermediary table that has a ForeignKey with unique enabled.