This is maybe not a fundamental question, but as curious as I am, I'm asking this myself for a long time.
A part of Django code is functions and is not directly related to Django models.
Let's take for example this function :
def make_random_raw_password(length=8):
# Do some stuff and return a 8 char randomised string
Technically, this function is not related to the User class, but functionally, it is!
Where would you write this function?
From my beginner point of view, I can see at least 2 options:
I create a myutils.py file which contains all these kind of functions then I import myutils.py in files like models.py / view.py to use these functions.
I move this function as a classmethod in my UserProxy class (since I can not directly modify the User class methods in Django) which gives me something close to:
-
class UserProxy(User):
class Meta(object):
proxy = True
#classmethod
def make_random_raw_password(cls, length=8):
# Do some stuff...
Which one would you use? Could you explain why? Thank you in advance.
Related
I had studied class methods in python but never really understood its application in Djnago. My coding life was going well without the use of it. But I came across a situation where class method will be useful.
My model:
class Example(models.Model):
post_count = models.IntegerField(default=0)
#classmethod
def total_counts(cls):
return cls.objects.values('post_views').annotate(sum logic)
In the above function, if I had used property decorator, i can only get a single object post_count because the object self is used. But if I use classmethod then I can count the post views of all the objects.
So my thought is that whenever we have to deal with all the objects instead of a self object we need class method. Is this correct?
Also, can we use this class method directly into our model serializer field just like we use property to serializer??
I'm writing my first application in Django, and now that the base is covered, I try to enhance a bit the admin part to ease up my life.
I have two classes in my model:
class Puzzle(models.Model):
puzzle_pieces = models.ForeignKey(PuzzlePieces,on_delete=models.CASCADE)
class PuzzlePieces(models.Model):
puzzle_pieces = models.CharField(max_length=255, default='empty')
with admin models in place too:
class PuzzlePiecesAdmin(admin.ModelAdmin):
class PuzzleAdmin(admin.ModelAdmin):
I want to define in the PuzzlePiecesAdmin class a custom action to display (reusing the format defined in PuzzleAdmin) all the puzzles which are linked to the selected puzzle_pieces
I know how to create custom actions,
def show_related_puzzles(modeladmin, request, queryset):
I've seen on the internet different ways to filter directly within the PuzzleAdmin class,
but not how to set the queryset from the outside.
But I don't understand how to launch the display of an instance of PuzzleAdmin limited to the queryset I will define within show_related_puzzles.
Could anyone explain me how to proceed?
Thanks in advance
OK, I found the start of an answer in the following:
https://stackoverflow.com/a/1652377/12505071
the standard changelist view accepts normal queryset filter parameters as GET arguments. So you can do:
/admin/puzzles/puzzle/?puzzle_pieces__pk=21
I finally found out by trial and error how to add another filter:
/admin/puzzles/puzzle/?difficulty__id__exact=35&puzzle__pk=560
Could anybody tell me how to add a second value for the same parameter?
My goal is to use the same function from multiple classes in Python.
I've seen discussion about mixins and inheritance etc but they all seem to come with caveats and cautions about doing things just right.
So I wondered if I could just call another plain old function that lives outsides the classes. It seems to work, but maybe I'm failing to understand something important?
So my question is - is this a valid approach to sharing a function between Python classes?
def clean_reference_url(self):
if not self.cleaned_data['reference_url']:
return ''
try:
if 'illegaltext' in self.cleaned_data['reference_url']:
raise Exception
except:
raise forms.ValidationError('You cannot put illegaltext in this field.')
return self.cleaned_data['reference_url']
class ContactForm(forms.ModelForm):
def clean_reference_url(self):
return clean_reference_url(self)
class TripForm(forms.ModelForm):
def clean_reference_url(self):
return clean_reference_url(self)
It's valid, but it's unnecessary to have the extra layer of wrapping. The mix-in approach is the simplest, but yes, it has some caveats (largely related to metaclasses), so if you want to avoid that, you can still set a method in multiple classes by just setting during the definition of each class. Keep the function definition the same, and change the classes to:
class ContactForm(forms.ModelForm):
clean_reference_url = clean_reference_url
class TripForm(forms.ModelForm):
clean_reference_url = clean_reference_url
Again, a mixin is even cleaner, e.g.:
class CleanableUrl: # Change name as appropriate
def clean_reference_url(self):
# ...
class ContactForm(CleanableUrl, forms.ModelForm):
# No need to talk about clean_reference_url at all
class TripForm(CleanableUrl, forms.ModelForm):
# No need to talk about clean_reference_url at all
and it's usually the most Pythonic approach, assuming it works for your scenario (no conflicting metaclasses on the base types).
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()
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.