Django model for a Postgres view - python

Edit: There seems to be some confusion about what I'm asking. That model is for the Postgres view that I created in migration 0009. I was under the impression that Django won't generate a migration for a model if it has the managed = False option. However, it's still trying to create it.
Also, I'm using Django 1.8 with Python 3.4.
I'm having trouble creating a Django model for a Postgres view, using these links as a guide: drdaeman and eceppda's answer in Can I use a database view as a model in django. I also looked up the Options.managed entry in Django's API docs. However, even with this, it's creating a migration that adds a table for the view's model.
This is my code so far:
foo/models.py
class RelevantModel(models.Model):
rebate_pool_total = models.OneToOneField('foo.VirtualTotal', null=True)
total = models.DecimalField(null=True, decimal_places=2, max_digits=32)
class VirtualTotal(models.Model):
relevant_model = models.ForeignKey('foo.RelevantModel')
total = models.DecimalField(null=True, decimal_places=2, max_digits=32)
class Meta:
managed = False
foo/migrations/0009_add_foo_view.py
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('foo', '0008_previous_migration'),
]
sql = """
create VIEW foo_virtualtotal AS
SELECT rest of view...
"""
operations = [
migrations.RunSQL('DROP VIEW IF EXISTS foo_virtualtotal;'),
migrations.RunSQL(sql)
]
What am I doing wrong?

Django does create a migration for each newly added table in your app regardless of whether it's a managed model or not. However there is a very important and subtle difference when you use the managed=False setting. The resultant migration is a dummy entry. It does not execute any SQL at all.
To confirm this add a new model that is unmanaged
class Dummy(models.Model):
something = models.IntegerField()
class Meta:
managed = False
now when you do makemigrations followed by sqlimigrate *myapp* *migration_number* you will see that it doesn't produce any sql.
If on the other hand, you do find that Django is trying to create a table for you, that usually means that you had the same model in existence earlier but at the time the model was managed. To confirm this, search your migrations folder for VirtualTotal which is the name of the model in question.

Related

Django migrations - how to apply newly applied rules to previously saved model instances?

Let's say I have the following model and I have already some instances of the model saved in the database -
class Comment(models.Model):
comment_text = models.CharField(max_length=255)
Now, if I want to change the max_length argument to 127 instead of 255 and apply the migrations, the older instances will still be in the database where some might have the length more than 127 which is not obeying the new migrations.
What is the best approach to migrate all previous data along with the new migration rules?
You have to write your migration and apply that before the generated migration (which alters the model).
For this purpose, you have first to write your migration. Place it in the migrations folder of the app. Then add its name to the dependencies field of the generated migration.
This example updates the uuid field of all MyModel Objects.
And is located in my_migration.py
# Generated by Django A.B on YYYY-MM-DD HH:MM
from django.db import migrations
import uuid
def gen_uuid(apps, schema_editor):
MyModel = apps.get_model('myapp', 'MyModel')
for row in MyModel.objects.all():
row.uuid = uuid.uuid4()
row.save(update_fields=['uuid'])
class MyMigration(migrations.Migration):
dependencies = [
('myapp', '0004_add_uuid_field'),
]
operations = [
# omit reverse_code=... if you don't want the migration to be reversible.
migrations.RunPython(gen_uuid, reverse_code=migrations.RunPython.noop),
]
Assume that your generated migration that alters MyModel is called GeneratedMigration.. edit the generated migrations file as below.
class GeneratedMigration(migrations.Migration):
dependencies = [
('myapp', 'my_migration'),
]
You can also use the run_before attribute.
Checkout djano documentation on writing migrations for more information.

Swagger prints only string datatype in Django Rest Framework using DefaultRouter

I'm struggling with generating documentation (or simply - with whole project). I'm barely new to Django and Python, so maybe my problem is quite trivial.
I've got bunch of well defined models in Django Rest Framework project, for working with them I'm using DefaultRouters - the project is starting now and is in the early phase of development. However, for have all actions listed, I've managed to install Swagger and run it. My problem is, that Swagger in all actions, in almost all parameters prints "string" data type. For example:
Model
class UseCaseVer(models.Model):
id = models.IntegerField(primary_key=True),
version_key = models.CharField(max_length=10,null=False,unique=True)
approv_date = models.DateField(default=timezone.now,null=False)
description = models.TextField()
class Meta:
abstract = False
Serializer:
class UseCaseVerSerializer(serializers.ModelSerializer):
date_joined = serializers.DateTimeField()
class Meta(object):
model = UseCaseVer
fields = ('id', 'version_key', 'approv_date', 'description')
Still gives me string in all fields. What is wrong?
Nothing is wrong. It gives you json. For example in python, to get actual values, you need to use json.loads()

Python Django: how to select the index type?

As stated in https://docs.djangoproject.com/en/1.10/ref/models/fields/#django.db.models.Field.db_index, you can add indexes to your table using db_index=True. But how do you force hash vs btree indexes?
Things have changed a lot since the question was asked. It is now possible to define an index in the model layer through the metaclass of your model (at least since django 1.11):
https://docs.djangoproject.com/en/dev/ref/contrib/postgres/indexes/
For example:
from django.contrib.postgres.indexes import HashIndex
from django.db import models
class User(models.Model):
name = models.CharField(max_length=100)
class Meta:
indexes = (HashIndex(fields=('name',)),)
Django has no implemented particular index type selection through models.
Workaround would be to create empty migration and write SQL statement in your migration for consistency
https://docs.djangoproject.com/en/1.10/howto/writing-migrations/
manage.py makemigrations --empty app
Inside of migration in operations put following
migrations.RunSQL('Query to add index')
RunSQL docs

django: default for non-nullable field

I am learning django by doing some tutorials and am currently on ownership in the effective-django tutorial. Unfortunately, the tutorials is written for an older version of django so some things are slightly different.
I am running into problems when adding an 'owner' field to my model Contact in models.py:
from django.db import models
from django.core.urlresolvers import reverse
from django.contrib.auth.models import User
class Contact(models.Model):
"""
Interface for contact class
"""
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
email = models.EmailField()
owner = models.ForeignKey(User)
def __str__(self):
return ' '.join([self.first_name, self.last_name])
def get_absolute_url(self):
return reverse('contacts-view', kwargs={'pk': self.id})
When trying to run 'makemigrations', i get warnings:
You are trying to add a non-nullable field 'owner' to contact without a default: can't do that
This makes sense for an existing database, so i tried flushing the database. However, the problem remains. What should be the default value for the field owner? I also tried making the field nullable but that gives me the error that field owner cannot be null.
I usually set to 1. In this case already existed records will be assigned to the first user.
If you want you can later reassign these records in the next data migration.
Well, it does not matter if the database is empty or not. It matters that you probably have already at least one migration that first creates the model, and then you try to run makemigrations that adds the field.
If these migrations are not too important for you (which I recon they are not since this is a tutorial), just delete all previous migrations and run makemigrations fresh.
Alternatively, you would have to manually change migrations, possibly provide a default value etc.

User-defined text fields in django admin

Django (and database) newbie here.
I'm trying to figure out a way to enable the creation of n custom text fields for a table (let's call it Book) using the admin interface. I would like a way for the user to define new text fields through the admin interface (instead of defining fields like CustomField1, CustomField2, etc, through a model followed by running manage.py syncdb).
Ultimately, I would want to create a separate table called CustomFields. The django admin user (who is not a programmer), would go and enter the custom text fields in this table (e.g. pubdate, isbn, country). Then, when doing data entry for a Book, they would hit "+" for every custom field they wanted, have them available in a dropdown, and add accompanying text for the custom field. The text entered for each field is specific to the parent Book.
Any suggestions? I have a feeling there's a simple solution that I'm somehow not grasping here.
Where you might run into problems is because Django will not recreate tables or columns based on changing model declarations. This means you're unable to add fields to a table at run-time without running the generated sql on your database manually. What you'll need, instead, is a way of defining custom fields, and linking them to your model.
What I'd suggest is the following:
class CustomField(models.Model):
name = models.CharField(max_length=32)
class Book(models.Model):
... fields
class CustomValue(models.Model):
field = models.ForeignKey(CustomField)
value = models.CharField(max_length=255)
book = models.ForeignKey(Book)
The validation on the above is fairly non-existant, and this doesn't allow you to define required custom fields for each model, but you should be able to come up with that part if you need it later on.
# taken and modified from django online tutorial
class CustomValueInline(admin.StackedInline):
model = CustomValue
extra = 3
class BookAdmin(admin.ModelAdmin):
fieldsets = [
# your fields in here
]
inlines = [CustomValueInline]
admin.site.register(Book, BookAdmin)
This will allow users to select up to 3 custom fields and values directly, with the option to add more if they wish.
Edit: Changed the answer to reflect further information in comments.
For the beginning you can create one model for the book and one for the text field, both connected through a foreign key relation. You can easily administrate this then through django's inline admins, which will enable you to add more text fields!
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
class TextField(models.Model):
text = models.TextField
book = models.ForeignKey(Book)
# admin.py
from django.contrib import admin
from models import TextField, Book
class TextAdmin(admin.TabularInline):
model = TextField
class BookAdmin(admin.ModelAdmin):
inlines = [TextAdmin]
admin.site.register(Book, BookAdmin)

Categories

Resources