django not taking in consideration model fields declared in __init__ - django

When using Model class like this:
class MyModel(models.Model):
def __init__(self, *args, **kwargs):
self.myfield = models.Field()
super(MyModel, self).__init__(*args, **kwargs)
It doesn't take into consideration myfield(in the admin form, when saving the object... )
But if i declare like that:
class MyModel(models.Model):
myfield = models.Field()
It works just fine.
Why?
Edit
I think i have a good reason: I have an abstract class UploadItem that defines a field called file like this: self.file = models.FileField(upload_to=upload_to) As you can see, in each child class, i have to call parent init method with appropriate upload_to variable(say 'videos' for Video model). So i cannot do it the normal way.

Because the Django ORM code does some serious meta-magic during class definition (just browse the django/db code to see how magic). You are doing an end-run around that magic by creating fields on the fly in the __init__() function.
Is there a really good reason for not creating the class in the normal way? If not, then do it the normal way. If you do have a good reason then get ready to get into the really deep end of the pool -- both of Python and Django.

Setting a dynamic path for the upload_to attribute is absolutely not a good reason for wanting to muck around with model field declaration.
This is something that Django handles already - if you set upload_to to a callable, you can return the correct value dependent on the model instance. See the documentation.

Related

Parametrize filtering manager queryset in Django

I want to implement kind of row level security for my model in Django. I want to filter out data as low as it's possible based on some requirement.
Now, I know I could create specified managers for this model as docs says but it seems for me that it needs to be static. I also know that I can create just method that will return queryset as I want but I'll be not sufficient, I mean the possibility to just get all data is still there and the simplest mistake can lead to leak of them.
So I found this post but as author said - it's not safe nor pretty to mess around with global states. This post is nearly 10 years old so I hope that maybe someone has come up with a better generic solution.
Here is piece of example to visualise what I need:
models.py:
class A(models.Model):
...
class B(models.Model):
user = models.ForeignKey(User)
a = models.ForeignKey(A)
And I want to create global functionality for getting objects of A only if instance of B with user as logged in user exists.
I came up with solution to just override get_queryset() of A manager like so:
managers.py
class AManager(models.Manager):
def get_queryset(self):
return super().get_queryset(b__user=**and_here_i_need_a_user**)
but I can't find hot to parametrize it.
==== EDIT ====
Another idea is to simply not allow to get querysets of A explicitly but only via related field from B but I can't find any reference how to accomplish that. Has anyone done something like that?
So you're sort of on the right track. How about something like this...
class AQuerySet(models.QuerySet):
def filter_by_user(self, user, *args, **kwargs):
user_filter = Q(b__user=user)
return self.filter(user_filter, *args, **kwargs)
class AManager(models.Manager):
queryset_class = AQuerySet
def filter_by_user(self, user, *args, **kwargs):
return self.get_queryset().filter_by_user(user, *args, **kwargs)
class A(models.Model):
objects = AManager()
# ...
then you can use it like this:
A.objects.filter_by_user(get_current_user(), some_filter='some_value')

Django admin short_description as callable

Is there a way to define short_description for a field in ModelAdmin as callable - sometimes one wants to provide extra dynamic information inside column names.
Or are there any ugly hacks that accomplish the same goals?
As far as I know/remember properties can only be defined on new-style classes, in the class definition. So your ModelAdmin field would have to resolve to a callable object with the desired property. This snippet looks to me like it should allow that:
https://djangosnippets.org/snippets/2447/
The field itself becomes a property that resolves to an instance of the VotesToday class, on which short_description is also a property.
Peter DeGlopper's answer provided the needed direction - despite the fact that since the djangosnippet's posting a lot of things have changed.
This is indeed working:
class MyAdmin(admin.ModelAdmin):
list_display = ('my_callable')
class MyCallable:
def __call__(self, obj):
return 42
#property
def __name__(self):
return 'Galaxy'
#property
def my_callable(self):
if not hasattr(self, __my_callable):
self.__my_callable = self.MyCallable()
return self__my_callable
Importantly enough, the MyAdmin object is not passed to the MyCallable.__call__() call - if you need access to it, pass it in the __init__ initializer yourself.

Using Django filters inside model function

The main purpose of a model is to contain business logic, so I want most of my code inside Django model in the form of methods. For example I want to write a method named get_tasks_by_user() inside task model. So that I can access it as
Tasks.get_tasks_by_user(user_id)
Following is my model code:
class Tasks(models.Model):
slug=models.URLField()
user=models.ForeignKey(User)
title=models.CharField(max_length=100)
objects=SearchManager()
def __unicode__(self):
return self.title
days_passed = property(getDaysPassed)
def get_tasks_by_user(self,userid):
return self.filters(user_id=userid)
But this doesn't seems to work, I have used it in view as:
tasks = Tasks.objects.get_tasks_by_user(user_id)
But it gives following error:
'SearchManager' object has no attribute 'get_tasks_by_user'
If I remove objects=SearchManager, then just name of manager in error will change so I think that is not issue. Seems like I am doing some very basic level mistake, how can I do what I am trying to do? I know I can do same thing via :Tasks.objects.filters(user_id=userid) but I want to keep all such logic in model. What is the correct way to do so?
An easy way to do this is by using classmethod decorator to make it a class method. Inside class Tasks:
#classmethod
def get_tasks_by_user(cls, userid):
return cls.objects.filters(user_id=userid)
This way you can simply call:
tasks = Tasks.get_tasks_by_user(user_id)
Alternatively, you can use managers per Tom's answer.
To decided on which one to choose in your specific case, you can refer James Bennett's (the release manager of Django) blog post on when to use managers/classmethod.
Any methods on a model class will only be available to instances of that model, i.e. individual objects.
For your get_tasks_by_user function to be available as you want it (on the collection), it needs to be implemented on the model manager.
class TaskManager(models.Manager):
def get_tasks_by_user(self, user_id):
return super(TaskManager, self).get_query_set().filter(user=user_id)
class Task(models.Model):
# ...
objects = TaskManager()

Best practice for static page elements in django

I have some page elements that don't change often and are displayed on every page like some adbars, footer content and such.
I want to change settings for this elements in my admin interface, so I have models for them.
Is there a best practice in django to deal with these elements?
Not really, no. You're describing a singleton pattern, so you might want to implement a singleton model type:
class SingletonModel(models.Model):
class Meta:
abstract = True
def save(self, *args, **kwargs):
self.id = 1
super(SingletonModel, self).save(*args, **kwargs)
def delete(self, *args, **kwargs):
pass
That will ensure that any model that inherits from that class can only ever have one member and that it can't be deleted. Other than that, I would suggest combining everything into just one model called something like SiteSettings with fields for header, footer, etc, instead of separate model for each.
You could use a context processor to add them to the context and use a simple caching mechanism so you don't have to hit the db every time like, http://eflorenzano.com/blog/2008/11/28/drop-dead-simple-django-caching/
Hard to answer - what exactly are you asking?
You can display these models in your base template. You can use caching to cut down on database calls.

get Django model class name into a field

I have tried multiple aproaches, but as I cannot use self in the class body, self.__class__.__name__ is not available. Would I need to override the save method to do this? Thanks for your help.
Your question is oddly phrased, so I'm going to come at it sideways.
Assume that you have defined a model Foo as follows:
from django.db import models
class Foo( models.Model ):
foo = models.IntegerField()
bar = models.IntegerField()
def klass( self ):
return self.__class__.__name__
Supposing you start a Django shell (python manage.py shell), you can do the following:
>>> from foo.models import Foo
>>> foo = Foo()
>>> print foo.klass()
Foo
This demonstrates that you can certainly use self.__class__.__name__ in the body of any method for model Foo. Thus you must have some other context where you need to dynamically determine the actual class name for your model, but not from an instance of your model.
If you've finished defining the model, then the following is legal:
>>> print Foo._meta.object_name
Foo
This mechanism would allow you to do introspection directly against the model, without having to create an instance of the model.
If this doesn't work for you, you must need this during the actual definition of the model. In that case, I would respectfully suggest that if you know you're defining the Foo model, you should just hardcode Foo wherever you need it. If you really need a dynamic way during the creation of your model to determine the name of the model ... could you describe the actual problem you're trying to solve, so that we can help you solve it?
This is more or less what I want:
class VFXContainer(models.Model):
classname=models.CharField(max_length=60,editable=False,blank=True)
parent=models.ForeignKey("self",blank=True,null=True)
def save(self, *args, **kwargs):
self.classname=self.__class__.__name__
super(VFXContainer, self).save(*args, **kwargs)
class Company(VFXContainer):
class Meta:
verbose_name_plural="companies"
class Project(VFXContainer):
pass
class CustomVFXContainer(VFXContainer):
pass
Now, what I dont know how to do, is I want to "override" the limit_choices_to option in the parent field on the child classes. What I want is CustomVFXContainer to be parented to any type of class, Project only to be parented by Company, and Company not to be parented at all. Im using this structure for the following reason. There is going to be a ton of fields that I want to be in all the subclasses, and I also have a separate Tasks models that link through a foreign key to the base VFXContainer Class (and thus is attachable to any of the child classes). Hope this makes it more clear on what Im trying to achieve, thanks for your help.