Django queryset ProgrammingError: column does not exist - django

I'm facing a big issue with django.
I'm trying to save object containing foreignKeys and 'ManyToMany` but i always get this error
ProgrammingError: column [columnName] does not exist
I've made serveral times all migrations but it doesn't works. I have no problem when i work with models that does not contain foreign keys. I have tried to delete the migration folder. It's seems my database doesn't want to update fields. I need to force it to create these column but i don't have any idea.
class Post(models.Model):
post_id = models.CharField(max_length=100,default="")
title = models.CharField(max_length=100,default="")
content = models.TextField(default="")
author = models.ForeignKey(Users, default=None, on_delete=models.CASCADE)
comments = models.ManyToManyField(Replies)
numberComments = models.IntegerField(default=0)
date = models.DateTimeField(default=timezone.now)
updated = models.DateTimeField(null=True)
def __str__(self):
return self.post_id
when i'm trying to retrieve this i have :
ProgrammingError: column numberComments does not exist
As i said before i made makemigrations and migrate, i even deleted the migration folder.
Any idea ?

To save a instance of the POST model with foreign key you need to insert the query object.
Code example:
user = Users.objects.get(pk = 1)
p = POST(
title = 'Hello',
...
author = user,
date = '2018-01-01'
)
p.save()
You don't need to create post_id column, django creates one for you automatically, and you can access that using .pk or .id
You neither need numberComments. You should calculate that from comments many to many relation. Well... you can have this on DB too.
Next, you cannot add a many to many relation on creation. Create the post first as above. Then query the comment you want to add, the add the object to the relation
r = Replies.objects.get(pk = 1)
p.comments.add(r)
Hope it helps

Related

How to get the first record of a 1-N relationship from the main table with Django ORM?

I have a Users table which is FK to a table called Post. How can I get only the last Post that the user registered? The intention is to return a list of users with the last registered post, but when obtaining the users, if the user has 3 posts, the user is repeated 3 times. I'm interested in only having the user once. Is there an alternative that is not unique?
class User(models.Model):
name = models.CharField(max_length=50)
class Post(models.Model):
title = models.CharField(max_length=50)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts', related_query_name='posts')
created = models.DateTimeField(default=timezone.now)
class Meta:
get_latest_by = 'created'
ordering = ['-created']`
I already tried with selected_related and prefetch_related, I keep getting multiple user registrations when they have multiple Posts.
user = User.objects.select_related('posts').all().values_list('id', 'name', 'posts__title', 'posts__created')
This does give me the answer I want, but when I change the created field to sort by date, I don't get the newest record, I always get the oldest.
user = User.objects.select_related('posts').all().values_list('id', 'name', 'posts__title', 'posts__created').distinct('id')
I'm trying to do it without resorting to doing a record-by-record for and getting the most recent Post. I know that this is an alternative but I'm trying to find a way to do it directly with the Django ORM, since there are thousands of records and a for is less than optimal.
In that case your Django ORM query would first filter posts by user then order by created in descending order and get the first element of the queryset.
last_user_post = Post.objects.filter(user__id=1).order_by('-created').first()
Alternatively, you can use an user instance:
user = User.objects.get(id=1)
last_user_post = Post.objects.filter(user=user).order_by('-created').first()

Django migration not picking up all the model fields

I created a new Django project on Windows 10, with the latest python and Django. After creating the model with a number of tables/models on one model it skipped some fields. After retrying it would not create a new migration script. I looked at the initial 0001_initial.py and the fields were missing in that file. I later added a single new field and that field migrated property but not the missing fields. All other models were successfully updated in the initial migration. This happens to be a SQLite DB.
The model that is missing fields:
class articles(models.Model):
""" articles"""
title = models.CharField (max_length=500)
teaser = models.CharField (max_length=500)
summary = models.CharField (max_length=1500)
byline = models.CharField (max_length=250)
category = models.SmallIntegerField
articlenote = models.CharField (max_length=1000)
author = models.CharField
publishedDate = models.DateTimeField
articleStatus = models.CharField(max_length=4)
articleText = models.TextField
last_update= models.DateTimeField
def __str__(self):
return self.name
The missing fields are : category, author, publishedDate, articleText
Interesting after trying to rerun the migration I was getting Missing Defaults errors on a new table I was creating.
Since this is a brand new project I can just blow away the database and the migration scripts and try again. I would like to knwo what is potentially causing this missing migration.
You are missing the initialization of the fields.
So instead of author = models.CharField, use author = models.CharField(max_length=256) and also for all the others, use the parentheses.

Check the existence of instance with Django ManyToManyField

Hello Awesome People!
How to check whether a similar record is already in database with ManyToManyField():
class Room():
created_by = models.ForeignKey(User)
allowed_users = models.ManyToMany(User,related_name='rooms_as_guest')
is_active = models.BooleanField(default=True)
In my view before creating a new room, I want to make sure there wasn't an active room with exactly the same participants.
The following example won't work, it's just to show you what I expect
allowed_users = User.objects.filter(id__in=request.POST.get("ids",[]))
if allowed_users:
Room.objects.get_or_create(
created_by = request.user,
allowed_users = allowed_users, # won't work
is_active = True,
)
How could I proceed to do what I want?
This is because allowed_users is a ManyToMany Field and so, Room needs to created before adding Users to it.
See official documentation here:
https://docs.djangoproject.com/en/2.1/topics/db/examples/many_to_many/#many-to-many-relationships
Now the other problem is we need to get an existing room with the same set of users, which is already answered in a similar question here:
Django filter where ManyToMany field contains ALL of list
Try below:
from django.db.models import Count
allowed_users = User.objects.filter(id__in=request.POST.get("ids",[]))
if allowed_users:
room = Room.objects.filter(allowed_users__in=allowed_users).annotate(num_users=Count('allowed_users')).filter(num_users=len(allowed_users)).first()
if not room:
room = Room.objects.create(created_by=request.user, is_active=True)
room.allowed_users.add(*allowed_users)
You can't create an object like that with a ManyToMany field because the object must exist before you can create an intermediary object that represents your m2m relationship. You would have to do something like:
allowed_users = User.objects.filter(id__in=request.POST.get("ids", [])).order_by('id')
room = None
for r in up.rooms.filter(is_active=True):
if list(allowed_users) == list(r.allowed_users.all().order_by('id')):
room = r # get the existing room
break
if not room:
room = Room.objects.create(
created_by = request.user,
is_active = True
)
room.allowed_users.add(*allowed_users)
You'd have to ensure allowed_users are in the same order as room.allowed_users however.
Check the docs for more info

Django: New Field Addition Gives Error while doing makemigration

I have a Post models for blog:
сlass Post(models.Model):
author = models.ForeignKey(User,
related_name="blog_posts",
on_delete=models.CASCADE)
........
........
And I want to add a Tag to the project, so I made a Tag model:
class Tag(models.Model):
tag_name = models.CharField(max_length=20,
blank=True)
def __str__(self):
return self.tag_name
I added this to the Post model:
tag = models.ForeignKey(Tag, related_name="blog_tag",
on_delete=models.CASCADE)
But makemigration give to me an error:
You are trying to add a non-nullable field 'tag' to post 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
So why i have to populate existing rows? And how i can to live the rows empty?
P.S. If i chose 1) i still have an error.
You already have data in your db and now you are adding a new column tag in your database table, which is also not null.
So django is saying that either you provide one-default data for the previously existing rows or you make this new column (tag) a null value (by adding null=True in arguments of the field) so that already existing rows will fill it with null.

Django Introduce a foreign key from a model CharField

I have migrated over 10,000 records from my old mySQL database to Django/sqlite. In my old mysql schema's Song table, the artist field was not a 1 to many field but was just a mysql varchar field. In my new Django model, I converted the artist field to a ForeignKey and used temp_artist to temporarily store the artist's name from the old database.
How do I create each Song instance's artist foreignkey based on the temp_artist field? I'm assuming I should use the manager's get_or_create method but where and how do I write the code?
my model below:
class Artist (models.Model):
name = models.CharField(max_length=100)
class Song (models.Model):
artist = models.ForeignKey(Artist, blank=True, null=True, on_delete=models.CASCADE, verbose_name="Artist")
temp_artist = models.CharField(null=True, blank=True, max_length=100)
title = models.CharField(max_length=100, verbose_name="Title")
duration = models.DurationField(null=True, blank=True, verbose_name="Duration")
You can write a custom management command that performs this logic for you. The docs provide good instructions on how to set it up. Your command code would look something like this:
# e.g., migrateauthors.py
from django.core.management.base import BaseCommand
from myapp import models
class Command(BaseCommand):
help = 'Migrate authors from old schema'
def handle(self, *args, **options):
for song in myapp.models.Song.objects.all():
song.artist, _ = models.Artist.objects.get_or_create(name=song.temp_artist)
song.save()
Then you simply run the management command with manage.py migrateauthors. Once this is done and verified you can remove the temporary field from your model.
Since you don't have a usable foreign key at the moment you would have to dig down to raw_sql. If you were still on mysql you could have used the UPDATE JOIN syntax. But unfortunately Sqlite does not support UPDATE JOIN.
Luckily for you you have only a few thousand rows and that makes it possible to iterate through them and update each row individually.
raw_query = '''SELECT s.*, a.id as fkid
FROM myapp_song s
INNER JOIN myapp_artist a on s.temp_artist = a.name'''
for song in Song.objects.raw(raw_query)
song.artist_id = s.fkid
song.save()
This might take a few minutes to complete because you don't have an index on temp_artist and name. Take care to replace myapp with the actual name of your app.
Edit1:
Though Sqlite doesn't have update JOIN, it does allow you to SET a value with a subquery. So this will also work.
UPDATE myapp_song set artist_id =
(SELECT id from myapp_artist WHERE name = myapp_song.temp_artist)
type it in the sqlite console or GUI. Make sure to replace myapp with your own app name. This will be very quick because it's a single query. All other solutions including my alternative solution in this answer involve 10,000 queries.
Edit 2
If your Artist table is empty at the moment, before you do all this you will have to populate it, here is an easy query that does it
INSERT INTO stackoverflow_artist(name)
SELECT distinct temp_artist from stackoverflow_song
note that you should have a unique index on Artist.name