Django Models: Common Ancestor Inheritance & Migration - python

I thought I would up my python game with Django a bit by developing a large scale business app for fun. I seen the need for a common ancestor approach to model inheritence and tried to implement it based on the official documentation. However, I keep getting this very annoying Message which I'm not sure what to do with.
Dj Version: Django 1.7
Py Version: Python 3.4.2
Message
$ python manage.py makemigrations
You are trying to add a non-nullable field 'businessentity_ptr' to business without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
Models.py
class BusinessEntity(models.Model):
title = models.CharField(max_length=180)
def __str__(self):
return self.title
class Business(BusinessEntity):
description = models.TextField(max_length=600)
claimed = models.BooleanField(default=False)
slug = models.SlugField()
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return self.description
What I've Tried, (which everyone will hate):
Deleting the DB & Re-migrating
setting a default value for all fields
Setting all fields to null = True
I have seen a hack around for this but I don't think it's a good approach. Maybe there is someone out there who understand Django Common Ancestors much better and point me in the right direction.

Since your parent model is intended to be abstract, you should mark it as such.
class BusinessEntity(models.Model):
title = models.CharField(max_length=180)
class Meta:
abstract = True
This prevents Django from creating a separate table for it, and therefore needing a _ptr field to point back to it from the subclass. Instead, the table for your subclass will be created to include the inherited field(s) directly.

Related

Creating models/database Django FK

I’m a student bachelor ICT and currently making a simple webapp. I have no experience with python and django, so im struggling a bit.
I have a running empty MARIADB in a Linux Kali VM.
I have made a ERD from my web app in visual paradigm. I exported the .dll and created the database in MySQL workbench. I used inspectdb to import the django/python code for my models.py.
So far so good.
Django offers the User module, so i didn’t make an own user class
(auth_user).
The problem is: How do I build the relation with the auth_user class between a class i created called “case”? It’s asking for a default FK value and i have no idea what i means... I googled everywhere, but just “don’t understand”.
“Class case“ i want to set a relation with a user class. Explanation: A user can create one or more Cases.
“Class item”. A case can have or more Items (under investigation).
The error I am receiving when declaring a FK in Class item with Case:
“You are trying to add a non-nullable field 'zaakid' to gegevensdrager without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py“
Default value!? I just want a row in Class Item referring to the Case primairy key ID. I have no clue what the default should be...
Example of the code:
class Case(models.Model):
registratienummer = models.CharField(db_column='Registratienummer', unique=True, max_length=10, validators=[RegexValidator(r'^[0-9]{10}$')])
onderzoeksname = models.CharField(db_column='Onderzoeksnaam', blank=False, max_length=255)
Made_by = models.ForeignKey('AuthUser', db_column='Aangemaakt Door', null=True, on_delete=models.SET_NULL)
class Item(models.Model):
merk = models.CharField(db_column='Merk', max_length=255)
type = models.CharField(db_column='Type', max_length=255, blank=True, null=True)
serienummer = models.CharField(db_column='Serienummer', max_length=255, blank=False)
imei = models.CharField(db_column='IMEI', max_length=15, blank=True, null=True, validators=[RegexValidator(r'^[0-9]{15}$')])
#gegevensdrager_soortsocode = models.ForeignKey('GegevensdragerSoort', models.DO_NOTHING, db_column='Gegevensdrager_SoortSoCode')
#zaakid = models.ForeignKey('Zaak', db_column='Zaak_referentie', on_delete=models.DO_NOTHING)
After that I tried the inspectdb > models.py. This works but I want to add constraints etc, but it doesn’t work for some reason. It’s giving me all kinds of traceback I can’t explain. But first things first, my post is long enough. Hope someone can help me out a bit, since I’m quite stressed out at the moment.
With kind regards,
Florus.
If the database is already populated with data and you add a new field to that data, then Django needs something to assign to the data already in the database. In this case, you had data that didn't already have a foreign key associated with them, so the requested default is what the program should automatically associate with the previous data.
If you remove the data from your database, this should run normally, or you can give it data to populate those Cases with and manually change it later.

Django exclude field from all queries

I am running Django on Heroku with zero-downtime feature. This means that during deployment there are two version of code running (old and new) on the same database. That's why we need to avoid any backward incompatible migrations.
It there a possibility to exclude a field from Django query on a given model?
Let say we have a model (version 1):
class Person(models.Model):
name = models.CharField()
address = models.TextField()
In some time in the future we want to move address to the separate table. We know that we should not delete a field for older code to work so Person model may look like (version 2):
class Person(models.Model):
name = models.CharField()
address = models.ForeignKey(Address)
_address = models.TextField(db_name='address')
This way if old code will query for address it will get it from Person table even if database has been migrated (it will be an old value, but let assume thats not a big issue).
How now I can safetly delete _address field? If we will deploy version 3 with _address field deleted then code for version 2 will still try to fetch _address on select, even if it's not used anywhere and will fail with "No such column" exception.
Is there a way to prevent this and mark some field as "non-fetchable" within the code for version 2? So version 2 will not delete field, but will not fetch it anymore and version 3 will delete field.
You can use custom object manager for defer your specific field/fields for all the queryset.
class CustomManager(models.Manager):
def get_queryset(self):
return super(CustomManager, self).get_queryset().defer('_address',)
class Person(models.Model):
name = models.CharField()
address = models.ForeignKey(Address)
_address = models.TextField(db_name='address')
objects = CustomManager()
after that in your any queryset against Person model will not include _address field in query by default.
Yes, you can do it:
QuerySet.defer():
"In some complex data-modeling situations, your models might contain a lot of fields, some of which could contain a lot of data (for example, text fields), or require expensive processing to convert them to Python objects. If you are using the results of a queryset in some situation where you don’t know if you need those particular fields when you initially fetch the data, you can tell Django not to retrieve them from the database." - docs
Entry.objects.defer("headline", "body")
OR
With django 1.8 onwards: use values_list. You can only include fields that you want. You can also use Queryset.only() and Queryset.defer() to refine your queryset queries. You can chain defer() calls as well
Entry.objects.values_list('id', 'headline')

Django model inheritance: Delete subclass keep superclass

When dealing whith model inheritance in django is it possible to remove a instance of model subclass, without removing the superclass itself?
Using the Django example, can you remove just the Resturaunt object and retain the Place object?
Yesterday I was looking for an answer to this question and I came up with this solution, which was enough for my problem but could be scaled up as needed.
Assuming you have a Restaurant and a Place django models, the way to delete a restaurant only without touching the row inside the Place's table is creating a "fake" Restaurant model like this:
class FakeRestaurant(models.Model):
place_ptr = models.PositiveIntegerField(db_column="place_ptr_id", primary_key=True)
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
class Meta:
app_label = Restaurant._meta.app_label
db_table = Restaurant._meta.db_table
managed = False
Now, you can retrieve objects from that table as if it had no bound external relationship:
place = Place.objects.get(pk=1)
restaurant = Restaurant.objects.get(pk=1)
fake_restaurant = FakeRestaurant.objects.get(pk=1)
fake_restaurant.delete()
fake_restaurant and restaurant won't exist anymore, place will remain untouched.
Cheers,
Davide
In Django 1.9 parameter keep_parents was added to model delete() function, so to keep parents just call:
restaurant.delete(keep_parents=True)
Docs: https://docs.djangoproject.com/en/1.10/ref/models/instances/#django.db.models.Model.delete
UPDATE:
Apparently, this feature is not working properly in Django 1.9, please see the comments.

Need some explanation regarding BaseCommentAbstractModel of django comment app

class BaseCommentAbstractModel(models.Model):
"""
An abstract base class that any custom comment models probably should
subclass.
"""
# Content-object field
content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'),
related_name="content_type_set_for_%(class)s")
object_pk = models.TextField(_('object ID'))
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
# Metadata about the comment
site = models.ForeignKey(Site)
class Meta:
abstract = True
def get_content_object_url(self):
"""
Get a URL suitable for redirecting to the content object.
"""
return urlresolvers.reverse(
"comments-url-redirect",
args=(self.content_type_id, self.object_pk)
)
I have two questions related to this model code.
models.TextField(_('object ID')) Object ID which probably is the verbose Name of this TextField ,How does it reflect in the database?
Why do Django relies on field abstract of Meta innerclass instead of using abc(AbstractBaseClass) module?
That is indeed that verbose name. I assume you understand that _ is the call to ugettext_lazy which is for localizing strings. This is the verbose name of the field. The verbose name is not represented in the database. The name of the field in the database would be object_pk.
I'm not a django dev so I can't speak with authority, but some things are obvious. ABC is new in Python 2.6. This is an issue because as of the most recent release the minimum python version was finally moved to 2.5. This has been being bumped quite quickly as of late. For example it was only on django 1.2 that python 2.4 became required. Abstract models have existed at least as far back as django 1.0 and I think even further back then that (though I can't recall for sure) So even if ABC would be suitable (which I'm not sure it is since the behavior of models is kinda complex), it wouldn't be suitable for django at this point due to the required python version.
Additionally there is some complexity in managing classes that represent the database rather than just data structures. I'm not sure how much this impacts abstract models but for example you can't perform field hiding on django attributes that are Field instances.

Having a field named 'created' within a model - Django

I have a model such as the following:
class Item(models.Model):
name = models.CharField(max_length=150)
created = models.DateTimeField(auto_now_add=True)
the admin class is the following:
class ItemAdmin(admin.ModelAdmin):
list_display = ('name', 'created')
the created field does not seem to exist
Is there some basic Django knowledge that I am missing or have forgotten?
When you say the field does not exist, do you mean that it is not showing on the admin change form? This is expected behaviour when using auto_now_add. If you want the field to get a default value on creation but still be editable, use default=datetime.datetime.now instead.
Strange. I tried out your example and it worked perfectly well (Django 1.2.1, Python 2.6.2)
Can you verify that:
The field exists in the database (fire a SQL query perhaps)
Check your admin.py (again) for any differences.
Update
#Daniel's answer is more likely to help the OP.
Just make sure not to forget registering the ItemAdmin in admin.py:
admin.site.register(Item, ItemAdmin)
However, the 'created' field would only be displayed in the Item's list page, as well as if you add an additional field such as:
updated = models.DateTimeField(auto_now=True)

Categories