Django ModelAdmin custom method obj parameter - python

In Django's document
Django Document
It has following code.
from django.contrib import admin
class AuthorAdmin(admin.ModelAdmin):
fields = ('name', 'title', 'view_birth_date')
def view_birth_date(self, obj ):
return obj.birth_date
view_birth_date.empty_value_display = '???'
I don't understand in the custom method view_birth_date(self, obj ) where this obj parameter came from?
Note in the last line, it called this function,
view_birth_date.empty_value_display = '???'
but did not pass any parameter for obj. I don't understand where how obj got a value.
Thanks!

I don't understand in the custom method view_birth_date(self, obj) where this obj parameter came from?
This method is called by Django's machinery and obj is passed also by it. It's just a predefined interface.
view_birth_date.empty_value_display = '???'
but did not pass any parameter for obj. I don't understand where how obj got a value.
This line has nothing to do with your question. Again, according to the interface, Django when looking at your method (function) looks for empty_value_display attribute to find out what value the developer expects to see when the current value is empty.
Yes, looks weird but it's just how Django works, how its creators made it work. They chose this interface -- you just have to use the docs to find out the interface.

Related

Django get_queryset method of a custom model manager has no effect on other built-in methods(get, filter, etc.)

I have created a model manager with a method that adds some string to the certain field and my goal is to apply this method every time when objects called. As I understand, this can be achieved by using the get_queryset method in the custom manager, however this only works if I call SomeModel.objects.all(). If I try to apply some filter, or get object by parametr, it simply returns me original data without my method applied.
models.py:
class BaseModelQuerySet(models.QuerySet):
def edit_desc(self, string):
if self.exists():
for obj in self:
if 'description' in obj.__dict__:
obj.__dict__['description'] += string
return self
class BaseModelManager(models.Manager):
def get_queryset(self):
return BaseModelQuerySet(self.model, using=self._db).edit_desc('...')
class BaseModel(models.Model):
objects = BaseModelManager()
class Meta:
abstract = True
Output in django shell:
>>> SomeModel.objects.all()[0].description
'Some example of description...'
>>> SomeModel.objects.get(id=1).description
'Some example of description'
People, what am I doing wrong, please help. Thanks thousand times in advance!
The model.objects.get method does not return a QuerySet, so the call is never handled by get_queryset. .get returns a single object, not an iterable of objects.
Therefore, you want to override the get method in your model manager to specially handle the case for .get.

What is the lifecycle of the methods for a python Django DetailView?

When you write a view that extends Django's DetailView you can override the various methods such as get_queryset(), get_object() and get_context_data()
I'm having difficulty in get_context_data in reading the 'object' attribute. Specifically it seems to exist, but is class 'object':
def get_context_data(self, **kwargs):
context = super(SectionTextDetailView, self).get_context_data(**kwargs)
if object:
print (str(object))
## in reality try some logic with the object here
else:
print("very bad!")
return context
(this prints "")
I suspect that get_object() is called after get_context_data()? Is that true?
My overall question is - what is the lifecycle (the order of the evaluation) of the methods in a View class, specifically the DetailView?
I looked at the reference entry for DetailView and SingleObjectMixin in the Django documentation and it doesn't seem to explicitly mention this.
You don't define anything called object here. The only thing with that name is the built-in Python type.
You should probably be using self.object. Looking at the code, get_object is called immediately on calling get(), so it should be available straight away.

Accessing objects with a foreign key relationship in Django?

I'm currently trying to modify the django-podcast module so the podcast's xml file is statically served instead of being generated on every request for it.
I am attempting to rewrite the channel's xml file every time an episode is modified, created, or deleted and to do so I'm using django signals. What I would like to do is something like this...
from django.db.models.signals import post_save, post_delete
from django.template.loader import render_to_string
def update_xml_file(sender, **kwargs):
f = open('channelrss.xml', 'w')
f.write(render_to_string('podcast/show_feed.html', {'object': sender.show}))
f.close()
class Show(models.Model):
...
class Episode(models.Model):
post_save.connect(update_xml_file)
post_delete.connect(update_xml_file)
...
show = models.ForeignKey(Show)
...
The problem I keep running into is that sender.show is a ReverseSingleRelatedObjectDescriptor and not an actual instance of the Show class. I also tried reloading the sender object using sender.pk as the primary key value like this...
Episode.objects.filter(pk=sender.pk)
but apparently sender.pk returns a property object and not an integer or string and I don't know how to get it's value, so I guess I have two questions.
How can I retrieve the instance of Show associated with the Episode? and what the heck is a property object and why does sender.pk return it?
Thanks ahead of time for your response!
Josh
You can try:
def update_xml_file(sender, instance=False, **kwargs):
f = open('channelrss.xml', 'w')
f.write(render_to_string('podcast/show_feed.html', {'object': instance.show}))
f.close()
when instance.show.name_field is name_field of the model.
I finally figured it out! This issue was due to my lack of knowledge on the arguments being sent to my signal handler.
The sender argument sent to my handler was actually a class object and not the instance itself. In order to retrieve the instance itself I needed to use kwargs['instance'] and in order to retrieve the Show instance I simply used kwargs['instance'].show
As a result I think I understand where the property object issue was coming from. Correct me if I'm wrong, but when trying to access a 'class' object instead of an 'instance of a class' object the properties aren't defined as string or integer values, but rather property objects that need to be defined.
Also as an additional note, the signal.connect() function doesn't need to be defined in the class's model and the way it is written above is somewhat deceiving. The way it's connected above will listen for any object's save or delete signal sent. In order to associate the function with signal's from only Episode objects I should have written it as...
post_save.connect(update_xml_file, sender=Episode)
post_delete.connect(update_xml_file, sender=Episode)
or by using a decorator as shown in Django's signal documentation.
Thanks again for all the help!
Josh

How to update() a single model instance retrieved by get() on Django ORM?

I have a function which currently calls Models.object.get(), which returns either 0 or 1 model objects:
if it returns 0, I create a new model instance in the except DoesNotExist clause of the function.
Otherwise, I would like to update the fields in the pre-existing
instance, without creating a new one.
I was originally attempting to
call .update() on the instance which was found, but .update()
seems to be only callable on a QuerySets. How do I get around
changing a dozen fields, without calling .filter() and comparing
the lengths to know if I have to create or update a pre-existing
instance?
With the advent of Django 1.7, there is now a new update_or_create QuerySet method, which should do exactly what you want. Just be careful of potential race conditions if uniqueness is not enforced at the database level.
Example from the documentation:
obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon',
defaults={'first_name': 'Bob'},
)
The update_or_create method tries to fetch an object from database
based on the given kwargs. If a match is found, it updates the
fields passed in the defaults dictionary.
Pre-Django 1.7:
Change the model field values as appropriate, then call .save() to persist the changes:
try:
obj = Model.objects.get(field=value)
obj.field = new_value
obj.save()
except Model.DoesNotExist:
obj = Model.objects.create(field=new_value)
# do something else with obj if need be
if you want only to update model if exist (without create it):
Model.objects.filter(id = 223).update(field1 = 2)
mysql query:
UPDATE `model` SET `field1` = 2 WHERE `model`.`id` = 223
As of Django 1.5, there is an update_fields property on model save. eg:
obj.save(update_fields=['field1', 'field2', ...])
https://docs.djangoproject.com/en/dev/ref/models/instances/
I prefer this approach because it doesn't create an atomicity problem if you have multiple web app instances changing different parts of a model instance.
I don't know how good or bad this is, but you can try something like this:
try:
obj = Model.objects.get(id=some_id)
except Model.DoesNotExist:
obj = Model.objects.create()
obj.__dict__.update(your_fields_dict)
obj.save()
Here's a mixin that you can mix into any model class which gives each instance an update method:
class UpdateMixin(object):
def update(self, **kwargs):
if self._state.adding:
raise self.DoesNotExist
for field, value in kwargs.items():
setattr(self, field, value)
self.save(update_fields=kwargs.keys())
The self._state.adding check checks to see if the model is saved to the database, and if not, raises an error.
(Note: This update method is for when you want to update a model and you know the instance is already saved to the database, directly answering the original question. The built-in update_or_create method featured in Platinum Azure's answer already covers the other use-case.)
You would use it like this (after mixing this into your user model):
user = request.user
user.update(favorite_food="ramen")
Besides having a nicer API, another advantage to this approach is that it calls the pre_save and post_save hooks, while still avoiding atomicity issues if another process is updating the same model.
As #Nils mentionned, you can use the update_fields keyword argument of the save() method to manually specify the fields to update.
obj_instance = Model.objects.get(field=value)
obj_instance.field = new_value
obj_instance.field2 = new_value2
obj_instance.save(update_fields=['field', 'field2'])
The update_fields value should be a list of the fields to update as strings.
See https://docs.djangoproject.com/en/2.1/ref/models/instances/#specifying-which-fields-to-save
I am using the following code in such cases:
obj, created = Model.objects.get_or_create(id=some_id)
if not created:
resp= "It was created"
else:
resp= "OK"
obj.save()
update:
1 - individual instance :
get instance and update manually get() retrieve individual object
post = Post.objects.get(id=1)
post.title = "update title"
post.save()
2 - Set of instances :
use update() method that works only with queryset that what would be returned by filter() method
Post.objects.filter(author='ahmed').update(title='updated title for ahmed')

where to put method that works on a model

I'm working with Django.
I have a model called Agrument. Arguments have sides and owners. I have a function that returns back the side of the most recent argument of a certain user.
like obj.get_current_side(username)
I've added this to the actual Argument model like this
def get_current_side(self, user):
return self.argument_set.latest('pub_date').side
I am starting to think this doesn't make sense because there may not be an instance of an Argument. Is this a place I would use a class method? I thought about making a util class, but I'm thinking that it makes sense to be associated with the Argument class.
It would make more sense to have instance methods on the User model:
def get_current_side(self):
try:
return self.arguments.latest('pub_date').side
except User.DoesNotExist, e:
return None
You can do this by extending the User model as explained here:
Extending the Django User model with inheritance
Edit: I'm not exactly sure which exception gets thrown.
This should be a method on a custom model manager:
# in models.py
class ArgumentManager(models.manager.Manager):
def get_current_side(self, user):
try:
return self.filter(user=user).latest('pub_date').side
except Argument.DoesNotExist:
return None
class Argument(models.Model):
# fields etc...
objects = ArgumentManager()
# Calling:
side = Argument.objects.get_current_side(user)
Alternaticely you can extend contrib.auth.user and add get_current_size() on it. But I wouldn't mess with it until I'm very confident with Django.
BTW: Most of the code in this page is wrong; for example user variable is not used at all on the OP's snipplet.
I think what you are looking for are model managers.
Django docs on managers with managers you can add a function to the model class instead of a model instance.

Categories

Resources