Deleting related objects deletes the ForeignKey object - django

I have the following models:
class User(models.Model):
user_question = models.ForeignKey(UserQuestion)
class Question(models.Model):
text = models.CharField(max_length=255)
class UserQuestion(models.Model):
answer = models.CharField(max_length=255)
question = models.ForeignKey(Question)
user = models.ForeignKey(User, related_name='questions')
When I run the query below the user model is also deleted
user.questions.all().delete()
Is there any way to delete the questions without deleting the user?
I tried iterating over the questions and that didn't work
questions = user.questions.all()
for an in questions:
answer.delete()
I thought the queryset was lazy so maybe I needed to evaluate it before deleting so I printed it and this did not work.
print questions
questions.delete()
I know that making the ForeignKey nullable would provide me with methods like clear and remove but I did not want to do this because I did not want any orphaned user questions.
I updated the ForeignKey as follows
class UserQuestion(models.Model):
answer = models.CharField(max_length=255)
user = models.ForeignKey(User, related_name='questions', null=True, on_delete=models.SET_NULL)
I ran makemigrations and migrate but when I ran the query below The question model was still deleted.
user.questions.all().delete()

question = models.ForeignKey(Question, related_name='answers', on_delete=models.SET_NULL, null=True)
untested, but should work.
Your issue is a DB related issue, when deleting a foreignkey the DB will try to delete the related row.
Read more on the other options on this great answer.

Related

Django Query - Return All objects that have been referenced at least once in a model

I have two models. Employees and Schedule
class Employee(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
phone = models.CharField(max_length=20)
class Scheduled(models.Model):
title = models.CharField(max_length=50)
description = models.TextField()
assigned_employee = models.ForeignKey(Employee, on_delete=models.CASCADE)
I want to return all employees that have been referenced at least once in Scheduled table.
Note: I have done it with 2 queries, but is there a way to do this in just one query?
Edit:
It looks like you should not use _set to go through the relationship, the correct answer is:
Employee.objects.filter(scheduled_set__isnull=False)
Original:
I have not tried it yet but maybe that should work.
Employee.objects.filter(scheduled_set__isnull=False)
Thanks #Guillaume for the clue. Although your solution has a slight mistake, but it gave me enough to reach to the exact solution. This query worked for me
Employee.objects.filter(scheduled__isnull=False)
Note: I am using the default related_name field which is scheduled_set.

How do you clone a model and its dependencies in Django 2.2?

How do I clone a model in such a way that it includes (clones) data on FK-related models? I've looked through various related questions on Stack, and found most of them are for old/outdated versions of Django. Here's some illustrative model code (with str methods, etc omitted for brevity):
class Tracking(models.Model):
entry = models.CharField(blank=False, max_length=50)
timestamp = models.DateTimeField(null=True, auto_now_add=True)
active = models.BooleanField(default=False)
class Author(models.Model):
entry = models.ForeignKey(Tracking, on_delete=models.CASCADE)
first_name = models.CharField(blank=False, max_length=50)
last_name = models.ImageField(null=True, blank=True)
class Scene(models.Model):
entry = models.ForeignKey(Tracking, on_delete=models.CASCADE)
location = models.CharField(blank=False, max_length=50)
image = models.ImageField(null=True, blank=True)
My desired result would be to clone an existing "entry" on the Tracking model, such that a new "entry" on a new row is created with its own PK, as well as cloned copies of "Author" and "Scene" data on their respective tables which also point to the new cloned "entry". Any pointers in the right direction would be helpful. I'm not finding anything in the django docs.
The answer, as it turns out, is a function written by Stephen G Tuggy, as shown in this post.
I was able to modify this code for my purposes and run it successfully in the shell. It's awesome. Big thanks to Stephen for this. The "has_key()" function is deprecated and needs to be replaced with "in", as in "if field.name in" etc. Otherwise it's all good as of Python 3.7/Django 2.2.

Django - negative query in one-to-many relationship

Given these models:
class Profile(models.Model):
name = models.CharField(max_length=50)
class BlogPost(models.Model):
name = models.CharField(max_length=50)
created_by = models.ForeignKey(Profile, related_name='posts')
class Comment(models.Model):
blog = models.ForeignKey(BlogPost, related_name='comments')
body_text = models.TextField()
created_by = models.ForeignKey(Profile, null=True, blank=True, on_delete=models.SET_NULL)
Given a profile, I want to find all of the blog posts created by that profile, where there are either no comments, or only the creator of the post has commented.
For example:
profile = Profile.objects.get(id={id})
profile.posts.exclude(~Q(comments__created_by=profile))
I thought .exclude(~Q(comments__created_by=profile) would exclude all posts where a comment exists that has been created by someone other than the profile, but that's not working out. (It is finding posts where the created_by is null, and also posts where the profile has commented along with other users - which I'm trying to exclude from the set)
What you need is this:
comments_by_others_in_profile_posts = Comment.objects \
.filter(blog__created_by=profile) \
.exclude(created_by=profile)
profile.posts.exclude(comments=comments_by_others_in_profile_posts)
You can also try it like this (i believe this way it can be a little bit faster, but need to see the queries EXPLAIN output):
profile.posts.exclude(id__in=comments_by_others_in_profile_posts.values_list('blog', flat=True))
Well you were almost there, just need to include the conditions from your instincts. A good way to go about this is to use the django shell and a bunch of test data that matches your permutations. For more complex queries, its a good idea to write a unit test first.
profile.posts.filter(Q(comments__isnull=True)|~Q(comments__created_by=profile, comments__created_by__isnull=False))

Sort by related field in Django ORM

I have a Notes and a NoteRefs fields where the NoteRefs has a foreign key to the Notes. I need to query the Notes but order by the related field (ie. the NoteRefs' start_ref field).
How might I do that through the django ORM? Here's kinda what works in SQL
SELECT
note.user_id,
note.content,
note.created,
note.modified
FROM noteref
INNER JOIN note
ON note.id = noteref.note_id
ORDER BY noteref.start_ref
I can't use Note.order_by('related_field'), because the related field isn't part of the Note Model. From what I can tell, that seems to be what the documentation says to do. How can I sort on the related field here?
EDIT: Model information
class Note(models.Model):
user = models.ForeignKey(User, db_index=True)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
class NoteRef(models.Model):
note = models.ForeignKey(Note, db_index=True)
_order = models.IntegerField(default=0)
book = models.IntegerField(max_length=2)
start_ref = models.IntegerField(max_length=8, db_index=True)
end_ref = models.IntegerField(max_length=8, db_index=True)
ref_range = models.IntegerField()
passage = models.CharField(max_length=50)
You should try Note.objects.order_by("noterefs__start_ref")
The documentation doesn't make this very clear, as it uses a ForeignKey to self, but it works.
Now, the docs also warn against the possibility of duplicate objects showing up if you have multiple NoteRefs for a single Note, so you should double-check this.

Optional foreign key in model related to a model which doesn't exist

I've attempted to add a second ForeignKey relationship to a Django Model which relates to a model which hasn't yet been created.
class Forms2012(models.Model):
"""
Employer forms for the 2012-13 tax year
"""
employer = models.ForeignKey('Employer', blank=True, null=True)
# employee model is yet to be implemented
employee = models.ForeignKey('Employee', blank=True, null=True)
name = models.CharField(
max_length=50,
choices=constants.FORM_CHOICES)
description = models.CharField(max_length=255)
datemodified = models.DateTimeField()
As you might expect this is giving a not installed or is abstract error. However I've been told it should be possible to validate this because that key is optional.
Could someone explain if this is possible, I've added the flags blank=True, null=True to the FK but model validation fails so I think I'm fighting a losing battle.
Why don't you make (temporary) dummy models?
I would recommend that you implement a "stub" Employee model eg
class Employee(models.Model):
pass
If you are using database migrations ( eg South ), which you should, it should be a simple matter to "fill out" the Employee class later on.
However, if you want a fairly involved "solution" to the problem, you can have a look at the accepted answer in this question. The author of the question & answer admits that it is ugly ( even though it works ). The "stub" solution is better.