Django migrate primary key from UUID to Slug - python

I have model AAA with UUID as primary key. Many models have relation to this model.
Now I want to migrate them to use Slug as this primary key, to keep slug value in database as a reference.
What will be the correct way to do that?
Thinking that this might be some multi-step migration. But having many tables that reference to AAA would like to avoid blocking whole db for much time or any other issues in production.
from django.db import models
from django_extensions.db.fields import AutoSlugField
from model_utils.models import UUIDModel
# Django models example
class AAA(models.Model):
id = UUIDField(primary_key=True, version=4, editable=False)
title = models.CharField(max_length=255, unique=True)
slug = AutoSlugField(populate_from='title', primary_key=False)
class BBB(models.Model):
aaa = models.ForeignKey(AAA, on_delete=models.CASCADE)
# ... other fields here ...

Related

How to insert Primary key using Django Models?

i have set Primary key in my Django Models and but When i am inserting data into MongoDB database ,the primary key is not inserted.by default Django is inseting primary key but in my case its not.i Don't know what i am Doing Wrong Here.i am using below code
My Model.py
class CategoryModel(models.Model):
id = models.AutoField(primary_key=True) #if i don't use this line,django should insert primary by default.i my other app its inserting by default
category_name = models.CharField(max_length=255)
when i insert data into my CategoryModel,the primary key is not inserted.i am using Following code to insert data
Views.py
CategoryModel.objects.create(
category_name =request.POST['category_name']
)
Here,category_name is inserted but Primary key is not inserted.I don't know what is the issue.i Will be thankful if any one can help me with this issue.
a = CategoryModel()
a.category_name = request.POST['category_name']
a.save()
and ID you must use some other name for id because id is default django field
id_out = models.BigAutoField(primary_key=False, db_index=True, unique=True, editable=False)
Depends on what kind of id you're looking for, I typically use uuid4:
from uuid import uuid4
from django.db import models
def gen_uuid() -> str:
"""Return a str representation of a uuid4"""
return str(uuid4())
class CategoryModel(models.Model):
uid = models.CharField(
unique=True,
primary_key=True,
default=gen_uuid,
max_length=36,
help_text="Example: c8daa3ac-3dd0-44e9-ba2a-b0cbd1c8d8ae.",
)
category_name = models.CharField(max_length=255)
This allows to customize your ID. However if you are sure to use UUID like above I'd recommend this implementation as it uses the proper field:
from uuid import uuid4
from django.db import models
class CategoryModel(models.Model):
uid = models.UUIDField(
unique=True,
primary_key=True,
default=uuid4,
help_text="Example: c8daa3ac-3dd0-44e9-ba2a-b0cbd1c8d8ae.",
)
category_name = models.CharField(max_length=255)
As it is explained by the Autofield doc, you do not need to set your id if you wish to have the default id behavior: Autofield
It should insert your id automatically if you don't specify anything.

Django Operational Error: foreign key mismatch

I have two models:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Category(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="categories")
name = models.CharField(max_length=30, unique=True, primary_key=True)
class Todo(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='todos')
# TODO: Add confirmation before deleting category
category = models.ForeignKey(Category, on_delete=models.CASCADE,
related_name="todos_in_category", null=True)
item = models.CharField(max_length=50)
added = models.DateTimeField(auto_now_add=True)
completed = models.BooleanField(default=False)
Previously, Category's PK was the default id, however, I changed it to the name field. When I ran the migrations, i received the operational error. Thinking that it was perhaps due to a conflict between the existing id fields and the new primary key, I cleared the data in the database but with no success. Any ideas as to what could be the issue here? Thanks!

Django MultipleObjectsReturned while using a legacy database

I am using Djano to develop a simple web app to display and manage database data. I hooked up a MySQL db and used inspectdb to auto generate a model based on the database tables and this is what I got back, which looks good.
# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
# * Rearrange models' order
# * Make sure each model has one field with primary_key=True
# * Make sure each ForeignKey has `on_delete` set to the desired behavior.
# * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from __future__ import unicode_literals
from django.core.exceptions import MultipleObjectsReturned
from django.db import models
class Booking(models.Model):
class Meta:
managed = False
db_table = 'Booking'
unique_together = (('hotelno', 'guestno', 'datefrom'),)
hotelno = models.OneToOneField('Hotel', models.DO_NOTHING, db_column='hotelNo', primary_key=True) # Field name made lowercase.
guestno = models.IntegerField(db_column='guestNo') # Field name made lowercase.
datefrom = models.DateTimeField(db_column='dateFrom') # Field name made lowercase.
dateto = models.DateTimeField(db_column='dateTo', blank=True, null=True) # Field name made lowercase.
roomno = models.OneToOneField('Room', models.DO_NOTHING, db_column='roomNo') # Field name made lowercase.
list_display =
#def __str__(self):
# return ("".join(hotelno) + "".join(guestno) + "".join(datefrom))
class Guest(models.Model):
guestno = models.AutoField(db_column='guestNo', primary_key=True) # Field name made lowercase.
guestname = models.CharField(db_column='guestName', max_length=255) # Field name made lowercase.
guestaddress = models.CharField(db_column='guestAddress', max_length=255, blank=True, null=True) # Field name made lowercase.
class Meta:
managed = False
db_table = 'Guest'
class Hotel(models.Model):
hotelno = models.AutoField(db_column='hotelNo', primary_key=True) # Field name made lowercase.
hotelname = models.CharField(db_column='hotelName', max_length=255, blank=True, null=True) # Field name made lowercase.
city = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'Hotel'
class Room(models.Model):
roomno = models.IntegerField(db_column='roomNo', primary_key=True) # Field name made lowercase.
hotelno = models.ForeignKey(Hotel, models.DO_NOTHING, db_column='hotelNo') # Field name made lowercase.
type = models.CharField(max_length=255, blank=True, null=True)
price = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'Room'
unique_together = (('roomno', 'hotelno'),)
In the admin.py file for this app I included the models like so, so that I could at least see the data up there.
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.contrib import admin
# Register your models here.
from .models import Hotel, Room, Guest, Booking
admin.site.register(Hotel)
admin.site.register(Room)
admin.site.register(Guest)
admin.site.register(Booking)
When I access the default Django admin page, I'll see the tables registered on admin page.
I click on Bookings and see the multiple records, without names (for other reasons), but if I click on one of them I get the MultipleObjectsReturned Error
I've read everything I could find, and the closest thing to a reason I could find for why this is happening has to do with there being composite keys in some of the models. But, again, I don't know if that's the actual reason, I could also be missing something? I don't know.
I guess the reason is that data of Booking table not consistent with your model declaration. Django's admin detail view retrieves model by primary key.
You marked hotelno as PK:
hotelno = models.OneToOneField('Hotel', models.DO_NOTHING, db_column='hotelNo', primary_key=True)
Since some data already exists in Booking table, you have to make sure that hotelno values (hotelNo column) are unique or you will get MultipleObjectsReturned exception for non-unique pk values. Also make sure you've read this part of the documentation https://docs.djangoproject.com/en/1.11/ref/models/options/#managed
It looks like your booking table does not have a primary key and inspectdb guessed wrong when affecting it to the hotelno column of your booking table.
When the admin try to get the record by it's id it get multiple result since different booking can reference the same hotel multiple time.
What I would do on the booking model :
change hotelno and roomno to ForeignKey
remove the primary on hotelno
The new problem is now you have a model with no primary key. Django does not allow that. If you can alter the MySQL table add a primary key column to it and alter the booking model accordingly. If you can't alter the table I see no easy way to make it works.

Not able to use foreign key from another app?

travelers.models
from django.db import models
class ShortInfoTraveler(models.Model):
name = models.CharField(max_length=200, blank=True)
email = models.EmailField(blank=True)
blogs.models
from django.db import models
from travelers.models import ShortInfoTraveler
class Title(models.Model):
shortinfotraveler = models.ForeignKey('ShortInfoTraveler')
title_text = models.CharField(max_length=255)
description = models.CharField(max_length=255, null=True, blank=True)
And When I run makemigrations, Terminal show following-
ERRORS: blogs.Title.shortinfotraveler: (fields.E300) Field defines a relation with
model 'blogs.ShortInfoTraveler', which is either not installed, or is abstract.
You should be setting your foreign key like this:
models.ForeignKey('travelers.ShortInfoTraveler')
If you want to use a string to set the foreign key relation.
Or you should just set ShortInfoTraveler without it being a string since you've imported it.
Setting it to "ShortInfoTraveler" is looking for the model in the current models file instead of your other app which you can see in the error message output back.

How to use UUID

I am trying to get unique IDs for my Django objects. In Django 1.8 they have the UUIDField. I am unsure how to use this field in order to generate unique IDs for each object in my model.
Here is what I have for the UUIDField
import uuid
from django.db import models
class MyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
class Person(models.Model):
...
unique_id = MyUUIDModel()
I can reproduce the id for the UUID model, but everytime I do I get the exact same id. For Example:
person = Person.objects.get(some_field = some_thing)
id = person.unique_id.id
id then gives me the same id every time. What is wrong, how do I fix this?
I'm not sure why you've created a UUID model. You can add the uuid field directly to the Person model.
class Person(models.Model):
unique_id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
Each person should then have a unique id. If you wanted the uuid to be the primary key, you would do:
class Person(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
Your current code hasn't added a field to the person. It has created a MyUUIDModel instance when you do MyUUIDModel(), and saved it as a class attribute. It doesn't make sense to do that, the MyUUIDModel will be created each time the models.py loads. If you really wanted to use the MyUUIDModel, you could use a ForeignKey. Then each person would link to a different MyUUIDModel instance.
class Person(models.Model):
...
unique_id = models.ForeignKey(MyUUIDModel, unique=True)
However, as I said earlier, the easiest approach is to add the UUID field directly to the person.
You need to use the class you created as a subclass when declaring your Person model like this:
import uuid
from django.db import models
class MyUUIDModel(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
class Person(MyUUIDModel):
...
This way Person becomes a subclass of MyUUIDModel and will inherit its id field definition.
EDIT: Actually I was wrong. It's not possible yet to implement it as DEFAULT_AUTO_FIELD as it has to inherit from IntegerField. Here's the ticket in the django project with feature request to make it possible. Once it's resolved I'll update my answer.
As of Django 3.2, if you want to use uuid as a pk for all your models on a project-wide level, you don't need a generic abstract model anymore. Just define DEFAULT_AUTO_FIELD setting
default value
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
so something like this should work
DEFAULT_AUTO_FIELD = 'django.db.models.UUIDField'
Or even better, create your own field.
DEFAULT_AUTO_FIELD = 'project.common.models.CustomUUIDField'
Where you also define uuid type etc.
As seen in the docs, it can also be applied on an app level.
class MyAppConfig(AppConfig):
default_auto_field = 'project.common.models.CustomUUIDField'
You can directly add the id field as a UUIDField in the Person model. There is no need for a separate MyUUIDModel.
I think you have confused it with the MyUUIDModel used in the UUIDField example where the id is a UUIDField. You can just use the below code and it will use UUIDs for id.
import uuid
from django.db import models
class Person(models.Model):
...
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
To use UUID in Django for a new model see Django Docs.
However, if you want to use it for the existing model (with unique=True) having data corresponding to it, you will not be able to do it directly by the above documentation. It will create migration errors.
To do it without losing the data follow all the steps carefully of this Django Documentation.
in model import uuid:
import uuid
in class model use:
class Article(TimeStampedModel):
uuid = models.UUIDField(editable=False, default=uuid.uuid4, unique=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='articles', null=True)
categories = models.ManyToManyField(ArticleCategory, blank=True)
title = models.CharField(max_length=500, null=True, blank=True)
body = RichTextUploadingField(config_name='portal_lobar_config')
image = models.ImageField(upload_to='article_images/', null=True, blank=True)
headline = models.BooleanField(default=True)
tags = models.ManyToManyField(ArticleTag, blank=True)
slug = AutoSlugField(max_length=500, populate_from='title', unique_with='created__month', null=True)
published = models.BooleanField(default=False)
published_at = models.DateTimeField(null=True, blank=True)
def __str__(self):
return self.title
class Meta:
ordering = ['-created']

Categories