Propper related objects creation within Django modelform - python

Suppose there are two models: Author and Book.
Sure enough Book has a foreign key to an Author.
There is a create view in which user gives a name of the author and uploads a file with the list of books it has.
So I am trying to figure out the best way to create a form.
Right now I have:
class AddForm(ModelForm):
books = FileField()
class Meta:
model = Author
def clean_books(self):
return [book.strip() for book in self.cleaned_data['books'].file]
The problem is where should I put the actual creation of Books model objects? Looks like it should be in a save method, something like:
def save(self, commit=True):
author = super().save(commit=True)
Book.objects.bulk_create([Book(author=author, title=book.title, ...) for book in self.cleaned_data['books']])
return author
But is it ok? What really annoys me is the commit argument. It's totally ignored and it may confuse others if they supply commit=False. How do I take into account commit argument and do not break the logic?

Take look at inline formsets. Using them you can add bunch of inline forms inside your main form. That formset will handle for you all processing of data and saving Book instances into database. It's like inline in django admin.

Related

When to use each model relationship in Django?

I've been reading through the Django documentation and looking over some of the other answers on the site for a couple of hours now, yet I still can't get it to sink in. I know this isn't Django specific, but the examples I use will be from a Django project.
My question boils down to when is it appropriate to use each:
Many-to-many relationships
Many-to-one relationships
One-to-one relationships
One-to-one, more or less makes sense to me.
Now for the other two. While I understand the differences between them in isolation, when it comes to using them practically in a project, I get confused. Here is an example:
class User(AbstractUser):
pass
class Listing(models.Model):
title = models.CharField(max_length=64)
description = models.TextField()
class Watchlist(models.Model):
user = models.ForeignKey(User, related_name='watchlist', on_delete=models.CASCADE)
item = models.ManyToManyField(Listing)
class Comment(models.Model):
user = models.ForeignKey(User, related_name='comments', on_delete=models.SET_NULL)
comment = models.TextField()
Would this be the correct use of Many-to-one(ForeignKey) and Many-to-many?
Should Watchlist.item be a ForeignKey? Or is M2M correct?
Wouldn't it simplify to make the 'Watchlist' part of the User class? (give them an empty list to populate with listing ID's)
Why is Watchlist.user not a One-to-one relationship, if each watchlist belongs to a single user, and a user can only have one list?
Apologies for my stupidity, I just can't get this to sink in!
Thank you.
edit: Context, the models are from a 'learning' project I was working on intended to be an auction site, similar to eBay. The watchlist is sort of a 'wish' list... for the user to watch an item, not for site to watch a user!
To explain it simply these django-models or objects represents tables in your database and the fields are like the columns in them. So with a one-to-one relation you can only have one row in one table relating to one row in another table. For example one user in the user table (represented by one row) can only relate to one row in a profile table. But your user can have many comments, so this would be a one-to-many/foreignkey relation (if you set unique=true on a fk, it will in practice function as 1:1). If the users can collaborate on writing comments, or for example as here on stackoverflow where users can edit other users comments, that would be a many-to-many relation.
Database design can be complicated/complex, especially using an ORM without basic knowledge of SQL and how it all works beneath. In general it requires a bit of planning even for a simple application.

Django Models Generic Relations

I'm confused with generic relationships in Django.
I have a comment model, and I want both Workflow and WorkflowItem models to be able to have multiple comments.
If I do:
class Workflow(models.Model):
comments = models.ManyToManyField(Comment)
class WorkflowItem(models.Model):
comments = models.ManyToManyField(Comment)
then what do I put in the comment class to link the comment to one of these based on which it is or do I need generic relationships?
Also say I want to put members who are part of the Workflow model, do i do
class Workflow(models.Model):
comments = models.ManyToManyField(Comment)
members = models.ManyToManyField(Person)
or something else?
As you mentioned that you need to link comment back to either Workflow/WorkflowItem, I believe you can structure your models as below
class Workflow(models.Model):
members M2M field
class WorkflowItem(models.Model):
fields
class Comment(models.Model):
name_of_your_generic_fk(Can be either Workflow/WorkflowItem or any content type for that matter)
fields
Using models structure like this you can trace from comment if it was made on Workflow/WorkflowItem.
You can obviously devise a better solution if you put more thought into it!! :)

How to think about Django's normal class based views vs. using a REST API

I've been writing a webapp with Django to replace a clumsy, spreadsheet based sports picking game that I play with some friends. I've learned a lot, and had a great time getting to know Django and how to build something like this from scratch.
I recently realized that I wanted to use something more powerful on the frontend (Ember, Angular, etc) with the end goal being a single page app. To that end, I installed Django REST Framework (DRF) and started reading the docs and following the tutorial. It's super interesting, and I'm finally starting to see why a client-server model with an API is really the only way to achieve the smooth interactivity that's all over now.
I'm trying to implement one of my class based views as an API endpoint, and I've been having a lot of trouble conceptualizing it. I thought I'd start with a simple, GET-only endpoint- here's the simple CBV I'm trying to replicate in API form:
class MatchupDetail(DetailView):
template_name = 'app/matchups.html'
context_object_name = 'pick_sheet'
def get_object(self):
#logic to find and return object
def get_opponent(self,username,schedule,week, **kwargs):
#logic to find and return the opponent in the matchup
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
#logic to pull the opponents details and set them in the context
I feel like I have a handle on this flow- a user clicks a link, and this view retrieves the object at the heart of the requested page, supplements it with content in the context, then renders it.
As I began thinking about turning this into an API endpoint, it didn't make a whole lot of sense. Should I be putting all the user-related data into a single JSON response? Or should the frontend basically handle the flow of this logic and the API simply be composed of a collection of endpoints- for example, one to retrieve the object, and one or more to retrieve what's now being passed in the context?
What prompted me to make this post was some trouble with my (super basic) API implementation of the above view:
class MatchupDetailApi(generics.ListAPIView):
queryset = Sheet.objects.all()
serializer_class = SheetSerializer
With serializer:
class SheetSerializer(serializers.ModelSerializer):
user = serializers.ReadOnlyField()
class Meta:
model = Sheet
I added the user field when I noticed that without it, the returned serialized Sheet objects are literally just the row in the database- an integer ID, integer foreign key to the User object, and so on. With a 'traditional' CBV, the entire objects are returned to the template- so it's very intuitive to access related fields, and with Django it's also easy to traverse object relationships.
Does a REST implementation offer the same sort of thing? From what I've read, it seems like I'll need an extension to DRF (django-rest-multiple-models) to return more than one model in a single response, which leads me to think I should be creating endpoints for every model, and leaving presentation logic to when I take care of the frontend. Is that typical? Or is it feasible to have an API endpoint that does return something like an object and several related objects?
Note: the basic endpoint above stopped working when I added the user to the SheetSerializer. I realized I should have a UserSerializer as well, which is:
class UserSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = User
However, when I try to browse the API, i get a TypeError that the first user isn't serializable. Specifically: <User: dkhaupt> is not JSON serializable. Isn't this what the UserSerializer is for?
Is it feasible to have an API endpoint that does return something like
an object and several related objects?
Yes!
And it sounds like you are off to a great start. I would structure it something like this:
class UserSerializer(serializers.ModelSerializer):
"""serializes a user"""
class Meta:
model = User
fields = ('id', 'first_name', 'last_name',)
class SheetSerializer(serializers.ModelSerializer):
"""serializes a sheet, and nests user relationship"""
user = UserSerializer(read_only=True)
class Meta:
model = Sheet
fields = ('id', 'sheet_name', 'user',)
I don't think you need django-rest-multiple-models for what you are trying to achieve. In my sketch (where I'm guessing fieldnames) you will serialize the sheet, and also the associated user object.
You can add fields from another related model using the source attribute.
for example:
class SheetSerializer(serializers.ModelSerializer):
user_id = serializers.ReadOnlyField(source='user.user_id')
username = serializers.ReadOnlyField(source='user.username')
class Meta:
model = Sheet
Here the serializer will return the information from the user model that is related to the Sheet model.

Django REST framework serializing model combinations

I'm programming an online game with a JavaScript client and I use Django REST framework for the backend. I have written a quest system for it.
My quests objects are dynamically created from a django model QuestTemplate which stores information like the Quest desription and the titel (the part that is the same for every user); and another model QuestHistory where I put the information about the state of quest for a certain user: so it has fields like user and completed. They also have some nested objects: Tasks and, Rewards which are created in a similar way to the the Quest objects.
I added a pure python class Quest that combines all the fields of those models, and then I wrote a Serializer for this class. The drawback is that I have to define all the fields again in the QuestSerializer
I have seen that for the ModelSerializer you can use a inner class Meta where you specifiy the model and . Is there also a way to do this with a normal python class instead of a model (with my Quest class).
http://www.django-rest-framework.org/api-guide/serializers#specifying-nested-serialization
Or:
Is it possible to specify more than one model in this inner class, so that it takes fields from my model QuestTemplate and some other fields from my model QuestHistory?
(I'm also not sure about whether this structure makes sense and asked about it here: django models and OOP design )
In the class Meta of the ModelSerializer you can specify only one Model as far as I know. However there are possibilities to add custom fields to the serializer. In your case you could maybe try with:
custom_field = serializers.SerializerMethodField('some_method_in_your_serializer')
You should add the method to your serializer like this:
def some_method_in_your_serializer(self, obj):
# here comes your logic to get fields from other models, probably some query
return some_value # this is the value that comes into your custom_field
And add the custom_field to fields in the class Meta:
class Meta:
fields = ('custom_field', 'all_other_fields_you_need')
Take a look in the documentation about SerializerMethodField for deeper understanding.

Setup Django Blog Comments

If I wanted to setup comments for a blog in Django, and I wanted people to be able to reply to comments (like a normal blog), meaning each comment would have to know if it's a comment on another comment or not, would I set the model fields like this?
from django.db import models
from django.contrib.auth.models import User
class Comment(models.Model):
post = models.ForeignKey(Post)
user = models.ForeignKey(User)
text = models.TextField()
date = models.DateTimeField()
reply_to = models.ForeignKey(Comment, blank=True, null=True)
Is that correct? And how would I display them in a template?
Writing a hierarchical comments application seems too easy at first look but believe me it is not that simple. There are too many edge cases and security issues. So if this is a real project i would suggest you to use disqus, any other hosted solution or (now deprecated) comments framework.
On the other hand if you are just trying to learn how things done or playing around, your code seems fair enough so far. But you should consider Django's built-in content types framework instead of a direct foreign key relationship. That way you can relate a comment object to any other object. (a blog post or another comment). Take a look at comment frameworks models.py and you will see it.
class BaseCommentAbstractModel(models.Model):
"""
An abstract base class that any custom comment models probably should
subclass.
"""
# Content-object field
content_type = models.ForeignKey(ContentType,
verbose_name=_('content type'),
related_name="content_type_set_for_%(class)s")
object_pk = models.TextField(_('object ID'))
content_object = generic.GenericForeignKey(ct_field="content_type", fk_field="object_pk")
Also take a look at RenderCommentListNodein comment framework template tags. You should write a recursive function in order to get and display hierarchical comments.
You have to consider cases like:
What will happen if a user deletes a comment?
How should we delete comments? Should we actually remove it from database or should we set an attribute like deleted
How should we deal with permissions and level of user access?
If we let anonymous users to comment, what information do we need from them.
How to check human validation? Is captcha enough?
Happy hacking.

Categories

Resources