Django south: access model's __unicode__() method - python

In django south data migration file, how can I access model's __unicode__() method?
Here is a simple example:
I have a Person class in models.py:
class Person(models.Model):
name = models.CharField(max_length=255)
def __unicode__(self):
return self.name
I then create a data migration with
python manage.py datamigration myapp check_person
In the data migration file, I have
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
for person in orm.Person.objects.all():
print person
Instead of printing the person's name, it prints
Person object
Person object
...
when I do:
from myapp.models import Person
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
print hasattr(orm.Person, '__unicode__')
print hasattr(Person, '__unicode__')
It prints
False
True
My question is that are those expected? Why can't south orm access __unicode__() method? How can I access it?
Edit:
Doing from myapp.models import Person and access Person objects directly by Person.objects.all() does not work for me, because the Person model will be removed from models.py in the next migration.

Instead of using orm.Person.objects.all(), you might want to import the original Person model and query on that instead. orm.Person is not the same thing as Person, it sort of depends on the big data structure models that defined at the end of each migration file to figure out the model information.
However, orm.Model would still work if your original model has been changed, that's why south could work with models that have changes, the model variable takes a snapshot of the models.py at the time it is created.
South doc explains this in details.
South doesn’t freeze every aspect of a model; for example, it doesn’t
preserve new managers, or custom model methods, as these would require
serialising the python code that runs those method (and the code that
depends on, and so forth).
Edit:
Sounds like OP would like to use the model __unicode__ property for certain purposes but the model would be deleted right after. Since an import error would occur in this case, I wouldn't bother using __unicode__ anymore, instead I would suggest handling this by manually accessing model fields using orm.Model(since __unicode__ is composed by field values anyway).

Related

Django: What is the simplest way to asynchronously execute some function when there is a change to the database?

I am familiar wıth the DBMS_ALERT feature in Oracle that sends an alert to the Operating System when there is a change to the database, and am somewhat familiar with database triggers in general. I have browsed through the django docs on signals, and inclined to use them if there is not a simpler way.
All I want to do is update or create an external file on the systems file system whenever there is an update or created record in the database. I would like this method to be called and defined right in models.py as depicted below.
models.py
from django.db import models
from django.shortcuts import reverse
class Entry(models.Model):
subject = CharField(max_length=20, unique=True)
content = models.TextField(blank=False)
class Meta:
ordering = ['subject']
def __str__(self):
"""String for representing the Model object."""
return self.subject
def get_absolute_url(self):
"""Returns the url to access a detail record for this entry."""
return reverse('entry_detail', args=[int(self.id)])
def ondbchange_updatecorrfile(self):
# this method would be called upon change in the model Entry, so it is already aware of object
util.save_entry(self.subject,self.content) # file corresponding to row is updated or created
What would be the simplest method to implement the ondbchange_updatecorrfile(self) method above?
I have looked at the below code from this source
models.py
class Entry(models.Model):
...
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=Entry)
def queue_task(sender, instance, created, **kwargs):
tasks.foo.delay(object=instance)
Then foo is some function in another class that would update the file. Since the database Model is the class that is aware of the change in the Model's underlying database, do we really need to use a signal if the desired function is already in the model and "self aware" of the database change?
Any help deeply appreciated. Please be specific since the devil is in the details.
The post_save signal is sent by the model Entry after a save, but according to this source, the signal does not include the model changes in its update_fields parameter. These must be manually inserted in the override of the function where the save() happens; which defeats the purpose. I initially assumed that the signal would automatically include this information.

generate django 2.0 model from a dict

Consider the following Django model:
class Person(models.Model):
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
I would like to define this class from a “static” dict (to be specific in my case it is generated from a file which will not change - so no need to makemigrations and migrate apart from first time).
Here is some pseudo-code to better explain what I want to achieve:
persondict = {‘first_name‘: models.CharField(max_length=255), ‘last_name’: models.CharField(max_length=255)}
class Person(models.Model):
#generate fields from persondict
This is just to start with. Note that you have to change the app_label inside the function, also this will work if you define it inside your models.py where you will use it, otherwise you will need to replace the '__module__' with the appropriate value.
def generate_person_model_from_a_dict(person_model_dict):
class Meta:
app_label = 'your_app_label_here'
person_model_dict.update({
'Meta': Meta,
'__module__': generate_person_model_from_a_dict.__module__,
})
Person = type("Person", (models.Model,), person_model_dict)
return Person
Meta.app_label is needed here to let the Django know which app the newly constructed model should be attached to, e.g. if you are creating it for the app blog and set the model's app_label to blog, then the Django will know, that the app has this model on it (just like you would define it in your blog/models.py)
__module__ is a special attribute of python objects (you can read details about it here https://docs.python.org/3/reference/datamodel.html ) To be brief, it lets Django to know which module your class belongs to, it uses it mostly to display various messages to it's user, see here: https://github.com/django/django/search?utf8=%E2%9C%93&q=module&type=
(just set it to your models.py like in the above example is therefore what Django pretty much expects)

How to use base classes in Django

I'm trying to alter an app I've created so that it is reusable. It's based around a single model which sites using the app will subclass. As it stands, my non-reusable version has the following kind of structure:
# models.py
class Document(models.Model):
contents = models.TextField()
date = models.DateTimeField()
# views.py
from .models import SiteModel
# ...
class MyView(ListView):
def some_method(self, list_of_pks):
model_vals = Document.objects.filter(pk__in = list_of_pks).values()
def perform_action(request):
obj_pk = request.POST.get('obj_pk')
obj = Document.objects.filter(pk = obj_pk)
MySignal.send(sender=Document, instance = obj)
#etc, etc
This works well enough. But my use case calls for different types of Document, one per site, that will have additional fields that aren't known in advance. Based on reading the documentation on abstract base classes, I thought the a reasonable solution would look like:
# models.py for the app
class BaseDocument(models.Model):
contents = models.TextField()
class Meta:
abstract = True
# models.py for a hypothetical site using the app
class SiteDocument(myapp.BaseDocument):
date = models.DateTimeField()
# other site-specific fields
What I don't understand is how to then reference the model in the app's views.py, forms.py, etc. I know BaseDocument.objects.all(), for example, won't return anything since it isn't connected to a database. Conversely, I can't have Document.objects.all() because Document hasn't been created yet and is specific to each site. Is an abstract base class not the correct solution, and if so, what is?
Edit:
It looks like using a OneToOneField may be best suited to my use case, although it looks like that precludes inheriting methods from the superclass and that BaseDocument.objects.all() won't list out all its children.
Alternatively, I was wondering if I could just add a get_document_model() method to my abstract base class, in the style of get_user_model()?
You can't query your abstract classes directly like that since they won't have managers, only the inherited classes. If you really must do inheritance, you can use a concrete base model and inherit from that at the cost of a join on every query.
Think long and hard about whether this is truly necessary, or if you can represent your data in a more generic way. Models make inheritance seem easy, but they're not magic. There are very real performance and complexity considerations.
It might be as easy as adding a type field to your model
class Document(models.Model):
DOCUMENT_TYPES = ['site', 'another', 'whatever']
document_type = models.CharField(choices=DOCUMENT_TYPES)
...
For more information about abstract vs concrete classes and querying, visit How to query abstract-class-based objects in Django?
I ended up going with a solution mentioned in my edit, namely creating a get_document_model() method inspired by get_user_model(). This gives me exactly the desired behavior.
# models.py in app1
from django.db import models
from django.apps import apps as django_apps
class BaseDocument(models.Model):
contents = models.TextField()
class Meta:
abstract = True
def get_document_model():
# exception handling removed for concision's sake
return django_apps.get_model(settings.DOCUMENT_MODEL)
# models.py in app2
from django.db import models
from app1.models import BaseDocument
class SiteDocument(BaseDocument):
date = models.DateTimeField()
Throughout views.py and elsewhere, I changed things that would have been of the form Document.objects.all() to BaseDocument().get_document_model().objects.all().

Django sync one-to-one models

I simplify my code structure, which contains two models:
# created by third part app, not Django one
# but we share same DB, so i have access to this one
class A(models.Model):
title = models.TextField()
# other fields ...
class Meta:
manage = False
class B(models.Model):
model_a = models.OneToOneField(A, related_name='+')
# other fields, to extend model A functionality
Is this a good way to extend third part app model A with my additional fields and methods? Now i have problem to sync this models true one-to-one field. Since I don't have access to trigger model A creation.
In ideal world i should have CarA and CarB. And CarB = CarA relation should be created if CarB exists.
I base this idea on Django 1.5 user extension. Is this clear enough? Or should i do something else?
You could use a property to create the B instance on access if it doesn't exist yet, ie,
class A(models.Model):
title = models.TextField()
# other fields ...
class Meta:
manage = False
#property
def b(self):
if not hasattr(self, "__bcache"):
self.__bcache, created = B.objects.get_or_create(model_a = self)
return self.__bcache
It seems like you're new to both Python and Django so let's explain quickly...
First, the "#property" part: it's a decorator that turns the following function into a computed attribute - IOW you use it as an attribute (myA.b.whatever), and under the hood it turns it into a method call (myA.b().whatever). It's not strictly required here, we would have used an explicit getter (the same method named get_a()) but it's cleaner that way.
Then our method implementation: obviously we don't want to hit the database each time someone looks up A.b, so
first we check if an attribute named __bcache ("b" "cache") is set on the current instance.
if not, we call B.objects.get_or_create(a_model=self) which will either retrieve the existing B instance for this A instance or create one if none exists yet and we store this B instance as self.__bcache so next call will retrieve it directly from __bcache instead of hitting the database.
and finally we return self.__bcache that is now garanteed to exists and point to the related B instance.

Should I be using controllers.py or #staticmethod in Django?

The person on our team who initially taught us django (and has subsequently left) utilized a controllers.py file for helper functions. A lot of these functions are directly related to classes. I prefer to use #staticmethod to house these helpers with the classes they're related to. For example, rather than doing this:
# controllers.py
def process_entry():
# do some exciting stuff that might not have to do with an instance
Called like this:
from myapp.controllers import process_entry
process_entry()
I'd prefer this:
# models.py
class Entry(models.Model):
name = CharField...
# some other fields
#staticmethod
def process_entry():
# do some exciting stuff that might not have to do with an instance
Called like so:
from myapp.models import Entry
Entry.process_entry()
Is there a preferred, standard way to deal with situations like this or is it just personal preference? Does anyone else utilize a controllers.py file? What goes in there?
Thanks.
EDIT:
Possibly a better example, commissions.
# models.py
class Commission(models.Model):
check = ForeignKey
payment = ForeignKey
# other fields
class Check(models.Model):
# fields
class Payment(models.Model):
# fields
Any time a Check or Payment instance is modified an adjustment as to be made to any related Commission instances or any time someone wants to manually run commissions, they can do so. So where should run_commissions() go? I prefer this, but apparently this shouldn't be encapsulated in a data-related model?
# models.py
class Commission(models.Model):
check = ForeignKey
payment = ForeignKey
# other fields
#staticmethod
def run_commissions():
# do stuff
Static methods are used for grouping related functions in one class (mostly for factory methods), beside that, there is no difference between static method and function.
BUT. In your example you are assigning behavior to DATABASE model. DATABASE models are not LOGIC models, and you should separate them from your app logic. Anyway, controllers is also a bad name in that matter.
I'm not sure what process_entry does, but if it's only changing one Entry entity, then it can be named: Entry.process(), but NOT Entry as DB model! just another Entry class. However if that function does more than just changing Entry, then it shouldn't be assigned to Entry entity, but made as a service function.

Categories