I use mysql to test model.
With django_comment_migrate helps, help_text saved to sql table field comment.
Original model.py below:
from django.db import models
class Foo(models.Model):
name = models.CharField(max_length=20, verbose_name='alias_name', help_text='alias_name')
Run python manage.py inspectdb got result below, lost help_text.
class App1Foo(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=20)
class Meta:
managed = False
db_table = 'app1_foo'
Related
I'm new to Django, and I'm trying to create a "game" model with two attributes:
A many-to-one field where multiple instances of the game model are associated with an instance of a custom user model.
A many-to-many field where instances of the game model are connected with multiple instances of words, and instances of the word model are connected with multiple instances of the game model
Top of my models.py model:
from django.db import models
from users.models import CustomUser
from django.contrib.postgres.fields import ArrayField
Game model:
class SortingGame(models.Model):
user_current_player = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, blank=True)
field_words = models.ManyToManyField(Word, related_name="field_sorting_games")
Word model:
class Word(models.Model):
str_word = models.CharField(max_length=50,null=True)
int_grade_level = models.IntegerField()
arrint_phonemes = ArrayField(models.CharField(max_length=50),null=True)
arrstr_graphemes = ArrayField(models.CharField(max_length=50),null=True)
int_num_syllables = models.IntegerField()
arrstr_syllables = ArrayField(models.CharField(max_length=50),null=True)
User model:
class CustomUser(AbstractBaseUser):
# must have the following fields for django
email = models.EmailField(verbose_name="email",max_length = 100,unique=True)
username = models.CharField(max_length = 30, unique = True)
date_joined = models.DateTimeField(verbose_name = "date_joined",auto_now_add=True)
last_login = models.DateTimeField(verbose_name = "last_login",auto_now = True)
is_admin = models.BooleanField(default=False)
is_superuser = models.BooleanField(default = False)
is_staff = models.BooleanField(default = False)
is_active = models.BooleanField(default = True)
first_name = models.CharField(max_length=15, blank=True)
last_name = models.CharField(max_length=30, blank=True)
spelling_level = models.IntegerField(default=1, unique=False)
time_played = models.IntegerField(default=0, unique=False)
percent_correct = models.IntegerField(default=0, unique=False)
admin.py:
from django.contrib import admin
from .models import Word, SortingGame
admin.site.register(SortingGame)
When I run python3 manage.py makemigrations and python3 manage.py migrate, it doesn't complain, but when I go to the admin page of my django site it says psycopg2.errors.UndefinedColumn: column "user_current_player_id" of relation "game_sortinggame" does not exist.
This makes me think the issue is with user_current_player in SortingGame (it worked fine before I added that attribute), but I've looked around on different forums to see what might be going wrong and I can't seem to figure it out. I tried starting from scratch with a new database, and it's still throwing the same exception. Any ideas would be appreciated—thanks!
Nathan!
First thing would be make sure that you have the app where CustomUser model is created in your settings.py file, at INSTALLED_APPS.
If so, please have a look at this folder (app) where you have CustomUser defined to verify if there is in deed a migrations folder there.
I suspect that Django in not aware of this app (not under INSTALLED_APPS) and therefore did not migrated it. So, your database is not finding the User Model connection.
That said, I would suggested you to keep your account model as defined by Django User and create another model with a direct relationship to it to deal with profile/game fields such as spelling level, percentage_correct and so on.
This would keep your Model "concerns" more organized later on.
if you did make a migrations before try to use (python manage.py makemigrations -appname)
Also after That you need to Add the module in your admin.py
from django.contrib import admin
from .models import *
admin.site.register(SortingGame)
... all other modules
I was trying to show the list of the User's to-do lists using view.html. The error says:
no such column:testapp_todolist.user_id".
But I don't understand where this column is and how it is related to the red line in my view.html:
{% for td in user.todolist.all %}
Can you please explain in details how do I add this column?
Here's my models.py:
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class ToDoList(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="todolist", default=0)
name = models.CharField(max_length=200)
def __str__(self):
return self.name
class Item(models.Model):
todolist = models.ForeignKey(ToDoList, on_delete=models.CASCADE)
text = models.CharField(max_length=200)
complete = models.BooleanField()
def __str__(self):
return self.text
Try solving this by writing this on your console:
python manage.py makemigrations
python manage.py migrate
Those commands are going to create tables for the TodoList model in the database.
user does not have the object todolist, todolist is a foreign key in Item. You should do:
list = ToDoList.objects.filter(user=YOUR_USER_HERE)
or
# if you want to get the todolist of the currently logged in user
list = ToDoList.objects.filter(user=request.user)
I would like to execute a single query in Django which retrieves related data, by foreign key, in multiple tables. At present I have to run a query on each table e.g. (House, Furniture, People) using the House number as a filter.
In SQL I can do this in one query like this:
SELECT house.number, house.number_of_rooms, furniture.type, people.name
FROM (house INNER JOIN furniture ON house.number = furniture.house_number)
INNER JOIN people ON house.number = people.house_number
WHERE (((house.number)="21"));
Can this be done in Django?
See example models below:
class House(models.Model):
number = models.CharField('House Number', max_length=10, blank=True, unique=True, primary_key=True)
number_of_rooms = models.IntegerField(default=1, null=True)
class Furniture(models.Model):
house_number = models.ForeignKey(House, on_delete=models.CASCADE, null=True)
type = models.CharField('Furniture Type', max_length=50)
class People(models.Model):
house_number = models.ForeignKey(House, on_delete=models.CASCADE, null=True)
first_name = models.CharField('First Name', max_length=50)
In your models add related_name arguments for foreign keys, so that you can retrieve the objects related to the House() instance.
class Furniture(models.Model):
house_number = models.ForeignKey(House, related_name='house_furniture', on_delete=models.CASCADE, null=True)
type = models.CharField('Furniture Type', max_length=50)
class People(models.Model):
house_number = models.ForeignKey(House, related_name='house_people', on_delete=models.CASCADE, null=True)
first_name = models.CharField('First Name', max_length=50)
Then run the migration using following commands.
python manage.py makemigrations
python manage.py migrate
Then create a new serializers.py module in the same app.
#import models Furniture, People, house
from rest_framework import serializers
class FurnitureSerializer(serializer.ModelSerializer):
class Meta:
model = Furniture
fields = ['type'] # if you want all the fields of model than user '__all__'.
class PeopleSerializer(serializer.ModelSerializer):
class Meta:
model = People
fields = ['first_name'] # if you want all the fields of model than user '__all__'.
class HouseSerializer(serializer.ModelSerializer):
house_furniture = FurnitureSerializer(many=True)
house_people = PeopleSerializer(many=True)
class Meta:
model = Furniture
fields = ['number', 'number_of_rooms', 'house_furniture', 'house_people']
Now, in your views.py you can simply query on model House and serializer the result with HouseSerializer().
#import models from models.py
#import serializer from serializers.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from rest_framework.generics import ListAPIView
class ListHouseView(ListAPIView):
serializer_class = HouseSerializer
queryset = House.objects.filter() #here you can apply filters on the fields of house model and user using related_name you can filter on other related models as well.
Now, simply call ad this in your app's urls.py
url_pattern = [
path('list-house/', ListHouseView.as_view()),
]
Make sure that have a path in your project's urls.py to reach this app's urls.py.
The usual Django way of dealing with this is Queryset.prefetch_related() and iterating through Python (unless you're using Postgres, which has its own solution of ArrayAgg). Given your models, it'll cost three queries, but you won't have to deal with de-normalized row results.
h = House.objects.prefetch_related('furniture_set', 'people_set').get(number='21')
for furniture in house.furniture_set.all():
print(furniture)
for person in house.people_set.all():
print(people)
prefetch_related() caches the results and does the "joining" in Python once the queryset is evaluated, so iterating through the reverse relationships won't incur additional queries, and you're free to structure/serialize the data however you like. The raw SQL from this is something like:
SELECT house.number, house.number_of_rooms FROM house WHERE house.number = '1'
SELECT furniture.id, furniture.house_number_id, furniture.type FROM furniture WHERE furniture.house_number_id IN ('1')
SELECT people.id, people.house_number_id, people.first_name FROM people WHERE people.house_number_id IN ('1')
But Django does that behind-the-scenes so that you can just deal with a model instance in Python.
When I push my django project to heroku I get "relation "weather_city" does not exist". weather is the name of the app and city is a model.
models.py
from django.db import models
# Create your models here.
class City(models.Model):
name = models.CharField(max_length=25)
def __str__(self):
return self.name
class Meta:
verbose_name_plural = 'cities'
I think you may have forgotten to make migrations.
heroku run bash
$ python manage.py migrate
Scenario:
I have a model, Customer
class Customer(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
company = models.CharField(max_length=100)
and now I updated the company attribute witha ForeignKey relationship as below,
class Company(models.Model):
name = models.CharField(max_length=100)
location = models.CharField(max_length=100)
class Customer(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
company = models.ForeignKey(Company)
What I need is, when the new migrations applied to the DB,corresponding Company instance must automatically generate and map to the company attribute of Customer instance.Is that possible? How can I achieve this ?
Let's start from your original model and do it step by step.
class Customer(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
company = models.CharField(max_length=100)
First you would have to keep the original field and create a new one, to be able to restore the old data afterwards.
class Customer(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
company = models.CharField(max_length=100)
_company = models.ForeignKey(Company)
Now you can create a first migration with manage.py makemigrations. Then you will have to create a data migration. Create the migration using manage.py makemigrations yourapp --empty and update the generated file:
from django.db import migrations
def export_customer_company(apps, schema_editor):
Customer = apps.get_model('yourapp', 'Customer')
Company = apps.get_model('yourapp', 'Company')
for customer in Customer.objects.all():
customer._company = Company.objects.get_or_create(name=customer.company)[0]
customer.save()
def revert_export_customer_company(apps, schema_editor):
Customer = apps.get_model('yourapp', 'Customer')
Company = apps.get_model('yourapp', 'Company')
for customer in Customer.objects.filter(_company__isnull=False):
customer.company = customer._company.name
customer.save()
class Migration(migrations.Migration):
dependencies = [
('yourapp', 'xxxx_previous_migration'), # Note this is auto-generated by django
]
operations = [
migrations.RunPython(export_customer_company, revert_export_customer_company),
]
The above migration will populate your Company model and Customer._company field according to Customer.company.
Now you can drop the old Customer.company and rename Customer._company.
class Customer(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
company = models.ForeignKey(Company)
Final manage.py makemigrations and manage.py migrate.
Sure, but you have to do three migrations and the fields cant be named the same thing as both need to exist at the same time. If you already have removed the company field in your real database you are SOL and will have to fix them manually.
First, add the Company model in a normal db migration, then do a data migration and have it run after the first db migration, then do another db migration removing the company field from the Customer model.
The db migrations you can do with manage.py makemigrations as usual, just add something like below in a migration file between them, here i named the new company ForeignKey field to company_obj
def fix_companies(apps, schema_editor):
Company = apps.get_model("myapp", "Company")
Customer = apps.get_model("myapp", "Customer")
for c in Customer.objects.all():
company, _ = Company.objects.get_or_create(name=c.name)
c.company_obj = company
c.save()
def rev(apps, schema_editor):
# the reverse goes here if you want to copy company names into customer again if you migrate backwards.
pass
class Migration(migrations.Migration):
dependencies = [
('myapp', 'XXXX_migration_that_added_company_model'),
]
operations = [
migrations.RunPython(fix_companies, rev),
]
Something to note is if you are going through a cycle of renaming/ deprecating fields, using RunPython would leave you pointing to old model fields that wouldn't exist anymore after you are done with your field changes.
To avoid this, you might want to go with RunSQL instead.
# Generated by Django 3.2.3 on 2022-02-09 04:55
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("<your_app>", "<0006_migration_name>"),
]
operations = [
migrations.RunSQL(f"""
update public.<table_name> set new_field = old_field + some_magic;
"""
),
]
Docs.