Django many-to-one create parent ("one") entry if not already exist - python

I have a comparable setup as the documentation of django describes for a many to one scenario.
https://docs.djangoproject.com/en/3.0/topics/db/examples/many_to_one/
from django.db import models
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
def __str__(self):
return self.headline
class Meta:
ordering = ['headline']
I have situations where the Reporter does not yet exist, but Article can be created for a non-existing reporter, so I want the Article model to make a Reporter if it doesn't exist yet. I guess what I need is a check if the Reporter already exists and if not create a new one. Is this the best way? Or does Django have a better, build in, method for this? Al reporters will have specific ID that is
I very new to Django and have trouble finding resources about this, probably because I'm missing terminology, so I some can point me in the right direction I would already be helped!

You're looking for the get_or_create or update_or_create function.
reporter, created_reporter = Reporter.objects.get_or_create(
email=reporter_email,
first_name=reporter_first_name,
last_name=reporter_last_name,
)
reporter.articles.create(...)
Additionally, if you make Reporter.email unique you can do the following which is more robust as it takes advantage of your databases uniqueness constraint.
reporter, created_reporter = Reporter.objects.get_or_create(
email=report_email,
defaults={
"first_name": reporter_first_name,
"last_name": reporter_last_name,
}
)
Doing that will check if the reporter exists based on the email and if it doesn't, then it'll create one using the default values.

Related

Django: Confusion with accessing database model's foreign key data

This is my first time working with Django and while working I have encountered with a confusion to create a particular statement in views that leads to my desired output. I have created a model 'Parents' which has data of a specific student (Foreign Key), and I am confused to access that student id for further process like working with Attendance, or Results of that specific student. Below are necessary codes and my trial to fetch data.
Models.py
class Students(models.Model):
id = models.AutoField(primary_key=True)
admin = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
gender = models.CharField(max_length=50)
address = models.TextField()
course_id = models.ForeignKey(Courses, on_delete=models.DO_NOTHING, default=1)
session_year_id = models.ForeignKey(SessionYearModel, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = models.Manager()
def __str__(self):
return self.admin.first_name + " " + self.admin.last_name
class Parents(models.Model):
id = models.AutoField(primary_key=True)
admin = models.OneToOneField(CustomUser, on_delete=models.CASCADE)
gender = models.CharField(max_length=50)
**student = models.ForeignKey(Students, on_delete=models.CASCADE)**
relation = models.CharField(max_length=255)
address = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
objects = models.Manager()
def __str__(self):
return self.admin.first_name + " " + self.admin.last_name
Here I have two models, Students model has all information regarding student and the other model is Parent model which has parent information with its specific student id.
Below is the views file code where I am trying to fetch student id of currently logged in parent,
def HOME(request):
stud_data = Parents.objects.filter(student__id = request.user.id)
print(stud_data)
return None
At the time of page reload, I am able to get an empty QuerySet[] as it is not able to find the id.
Kindly help me finding the solution to this problem, so that I can continue thinking about the development.
Thanks :)
As you mentioned here, you are looking for Student data for currently logged in Parent. Hence you can look for Student data directly from Parent object. Like this:
stud_object = request.user.parent.student
This relation works because Parent has a OneToOne relation with CustomUser (I assume Authentication's custom User model), hence you should get Parent object from request.user.parent (reverse relation for OneToOne). Also, student field is a ForeignKey of Parent model.
Addionally, I think the relation between Parent and Student should be ManyToMany, because a Student can have multiple parents and one parent can have multiple students (or children).
There are two possibilities:
The View code that you have attached should be returning stud_data not None, but I am assuming that you know this and this current state of the code is just for debugging purposes.
The request.user.id contains a value that doesn't belong to any student's ID in the database. As you are using filter, it's not going to complain about it and just return you an empty QuerySet. I'd suggest using the get() filter here which raises the DoesNotExist exception and would help in debugging as well.
def home(request):
stud_data = Parents.objects.get(student__id = request.user.id)
return stud_data
Hope it helps!
Best of luck with your new journey!

Automatically create predefined objects in database for ManyToMany?

how would I go about automatically generating a list of objects in the database for DJango?
Example model:
#models.py
class Book(models.Model):
BOOKS = (
('0','Secret Life of Bees'),
('1','Pride and Prejudice')
)
name = models.CharField(max_length=100, choices=BOOKS)
def __str__(self):
return self.name
class Library(models.Model):
librarian = models.OneToOneField(UserProfile, on_delete=models.CASCADE, primary_key=True)
books = models.ManyToManyField(Book)
How would I automatically add all the books to the database so I don't have to manually add them using the admin control panel?
Update! To anyone who doesn't know how to do this, you can call Object.objects.bulk_create(Object([Object(property=propertyInfo),Object(property=propertyInfo)]).

Sort by aggregated foreign field in Django ModelAdmin changelist

In my current project I am trying to set up a simple testing app in Django. For management I use the generated Django admin, but I struggle to include a sortable computed field with best test result in changelist view of a model.
My models are as follows (simplified):
class Candidate(models.Model):
name = models.CharField(max_length=255, null=False)
email = models.EmailField(unique=True, null=False)
class Test(models.Model):
candidate = models.ForeignKey(Candidate, on_delete=models.CASCADE, null=False)
result = models.PositiveIntegerField(null=True)
class Question(models.Model):
text = models.TextField(null=False)
correct_answer = models.CharField(max_length=1, choices=OPTIONS, null=False)
class Answer(models.Model):
test = models.ForeignKey(Test, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='answers')
answer = models.CharField(max_length=1, choices=Question.OPTIONS, null=True)
A candidate may have multiple tests and I want to display a field with his best result in the changelist view and be able to sort by it. The result is a percentage of correct answers (Answer.question.correct_answer == Answer.answer) out of all answers with the same test FK.
Discovered I cannot use a custom computed field defined by a function, because Django then cannot sort by it as sorting needs modification of a queryset which translates directly to SQL. So I added the Test.result field with calculated percentages (which denormalized the scheme :-/ ) and try to add annotated field in queryset with SELECT MAX(Test.result) FROM Test WHERE Test.candidate = {candidate} for every candidate, but cannot find a way how to do it.
The problem is, that the query needs reversed foreign key mapping, because of the 1:M mapping of candidate:test and I haven't found a way how to implement it. This is as far as I got:
class CandidateAdmin(admin.ModelAdmin):
list_display = ['name', 'email','best_result']
search_fields = ['name', 'email']
def get_queryset(self, request):
queryset = super().get_queryset(request)
queryset = queryset.annotate(
_best_result = models.Max('tests_result')
)
return queryset
def best_result(self, obj):
return obj._best_result
But Django doesn't understand my attempt use MAX on reversed foreign key search of tests_result. Could you advise? Or if I missed a way how to add custom sorting, so I don't need to keep the calculated test result in the database while still sorting by it, I'd be grateful for any hint.
In the end I created a database view with the query, added it to my models with managed = False in Meta and used that instead. Works like a charm.

What type of database relationship should I use?

from django.db import models
class Reporter(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
def __str__(self):
return "%s %s" % (self.first_name, self.last_name)
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
def __str__(self):
return self.headline
class Meta:
ordering = ('headline',)
I found this one-to-many relationship example in the Django docs. How would I go about doing another class called Magazine. In the class magazine, it would contain many articles written by many reporters. Would I still use a many-to-one relationship, or would I need a one-to-one relationship?
This is either a ForeignKey from Article to Magazine, or a ManyToManyField between Article and Magazine. This depends on whether an article can occur in multiple magazines (frequently journalists sell the same article to different magazines, those are then, except for some formatting, word-by-word the same).
In case every article occurs in one magazine, then we thus implement it like:
class Article(models.Model):
headline = models.CharField(max_length=100)
pub_date = models.DateField()
reporter = models.ForeignKey(Reporter, on_delete=models.CASCADE)
magazine = models.ForeignKey(Magazine, on_delete=models.CASCADE)
def __str__(self):
return self.headline
class Meta:
ordering = ('headline',)
In case an article can remain unpublished, then you also probably have to set null=True, in the ForeignKey constructor.
In case of a ManyToManyField, django will implicitly create a table like article_magazine that stores mappings from Articles to Magazines (but if you query, you will only obtain the Magazines).
The relation is very probably not a OneToOneField. In fact a OneToOneField is a ForeignKey with unique=True. This thus means that no two Articles are published in the same Magazine. Therefore it means that every Magazine has either no or exactly one Article related to it.
That really depends on what you want to do...
You have to be more specific, and tell us what your problem really is.
If you have n Articles in your Magazine, and each Article only occur
in one Magazine, you have to use one-to-many.
If the Article can occur in multiple magazines, you have to use
many-to-many relationship.
That's not actually a python question, by the way...
According to your need i think you should refer to Many-to-many relationship.
In you think, an Article can be published using multiple reporter objects, and a Reporter has multiple Article objects then refer to https://docs.djangoproject.com/en/2.0/topics/db/examples/many_to_many/
If you Logic supports one reporter per article and multiple article per reporter then visit:
https://docs.djangoproject.com/en/2.0/topics/db/examples/many_to_one/.

What's the straightforward way to implement one to many editing in list_editable in django admin?

Given the following models:
class Store(models.Model):
name = models.CharField(max_length=150)
class ItemGroup(models.Model):
group = models.CharField(max_length=100)
code = models.CharField(max_length=20)
class ItemType(models.Model):
store = models.ForeignKey(Store, on_delete=models.CASCADE, related_name="item_types")
item_group = models.ForeignKey(ItemGroup)
type = models.CharField(max_length=100)
Inline's handle adding multiple item_types to a Store nicely when viewing a single Store.
The content admin team would like to be able to edit stores and their types in bulk. Is there a simple way to implement Store.item_types in list_editable which also allows adding new records, similar to horizontal_filter? If not, is there a straightforward guide that shows how to implement a custom list_editable template? I've been Googling but haven't been able to come up with anything.
Also, if there is a simpler or better way to set up these models that would make this easier to implement, feel free to comment.
How about making ItemType a ManyToManyField for Store?
To me it seems logical that if you're changing the ItemTypes available in a Store, you're changing a property of the Store (not the ItemType).
e.g.:
from django.db import models
class ItemGroup(models.Model):
group = models.CharField(max_length=100)
code = models.CharField(max_length=20)
class ItemType(models.Model):
item_group = models.ForeignKey(ItemGroup)
type = models.CharField(max_length=100)
class Store(models.Model):
name = models.CharField(max_length=150)
item_type = models.ManyToManyField(ItemType, related_name="store")
# admin
from django.contrib import admin
class StoreAdmin(admin.ModelAdmin):
list_display=('name', 'item_type',)
list_editable=('item_type',)
for model in [(Store, StoreAdmin), (ItemGroup,), (ItemType,)]:
admin.site.register(*model)
I get an error here:
File "C:\Python27\lib\site-packages\django\contrib\admin\validation.py", line 43, in validate
% (cls.__name__, idx, field))
django.core.exceptions.ImproperlyConfigured: 'StoreAdmin.list_display[1]', 'item_type' is a ManyToManyField which is not supported.
Which I solved by commenting out lines 41-43 in django.contrib.admin.validation:
#if isinstance(f, models.ManyToManyField):
# raise ImproperlyConfigured("'%s.list_display[%d]', '%s' is a ManyToManyField which is not supported."
# % (cls.__name__, idx, field))
Probably not the ideal solution, but it seemed to work for me.

Categories

Resources