Beginner: Trying to understand how apps interact in Django - python

I just got done working through the Django tutorials for the second time, and am understanding things much more clearly now. However, I'm still unclear how apps inside a site interact with one another.
For example, lets say I'm writing a blog application (a rather popular activity, apparently). Blog posts and comments tend to go together, and yet they are distinct enough that they should be built into separate apps, as is the general philosophy of Djano development.
Consider the following example. In reality I would not actually write the comment app myself, as good code for that already exists on the web, but this is for demonstration/practice purposes:
mysite/blog/models.py
from django.db import models
class post(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
content = models.TextField()
mysite/comments/models.py
from django.db import models
from mysite.blog.models import post
class comment(models.Model):
id = models.AutoField()
post = models.ForeignKey(post)
author = models.CharField(max_length=200)
text = models.TextField()
Is what I wrote above, importing a model from another app and setting it as a foreign key, how Django apps interact? Or is there a different/better method for the apps that comprise a site to interact?
Update
Per the recommendation in one response, I'm reading the documentation for contrib.contenttypes. If I'm reading this correctly, I could rewrite my example comment app like this:
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contentypes import generic
class comment(models.Model):
id = models.AutoField()
author = models.CharField(max_length=200)
text = models.TextField()
content_type = models.ForeignKey(ContentType)
content_object = generic.GenericForeignKey(content_type, id)
Would this be correct?

Take a look at django's built-in contenttypes framework:
django.contrib.contenttypes
It allows you develop your applications as stand-alone units. This is what the django developers used to allow django's built-in comment framework to attach a comment to any model in your project.
For instance, if you have some content object that you want to "attach" to other content objects of different types, like allowing each user to leave a "favorite" star on a blog post, image, or user profile, you can create a Favorite model with a generic relation field like so:
from django.db import models
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
class Favorite(models.Model):
user = models.ForeignKey(User)
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
In this way you can add a Favorite star from any user to any model in your project. If you want to add API access via the recipient model class you can either add a reverse generic relation field on the recipient model (although this would be "coupling" the two models, which you said you wanted to avoid), or do the lookup through the Favorite model with the content_type and object_id of the recipient instance, see the official docs for an example.

"Is what I wrote above, importing a model from another app and setting it as a foreign key, how Django apps interact?"
Yep. Works for me.
We have about 10 applications that borrow back and forth among themselves.
This leads to a kind of dependency in our unit test script.
It looks like this.
"ownership". We have a simple data ownership application that defines some core ownership concepts that other applications depend on. There are a few simple tables here.
"thing". [Not the real name]. Our thing application has data elements owned by different user groups. There are actually several complex tables the model for this app. It depends on "ownership".
"tables". [Not the real name]. Some of our users create fairly complex off-line models (probably with spreadsheets) and upload the results of that modeling in "tables". This has a cluster of fairly complex tables. It depends on "ownership".
"result". [Not the real name]. Our results are based on things which have owners. The results are based on things and tables, and are responses to customer requests. This isn't too complex, perhaps only two or three core tables. It depends on "things" and "table". No, it doesn't completely stand-alone. However, it is subject to more change than the other things on which it depends. That's why it's separate.
"processing". We schedule and monitor big batch jobs. This is in this application. It's really generic, and can be used in a variety of ways. It completely stands alone.
"welcome". We have a "welcome" app that presents a bunch of mostly static pages. This doesn't have too many tables. But it's on it's second incarnation because the first was too complex. It completely stands alone.
The only relationship among the dependent apps is some table names. As long as we preserve those tables (and their keys) we can rearrange other apps as we see fit.

There's nothing wrong (imho) with making some app dependent on another. After all, apps are just operations on a set of models. you just have to always be aware of which app depends on which app (I guess you could call that a dependency map).
You can achieve loose coupling with the contenttypes framework. It allows an app to be truely portable/pluggable yet still integrated with other applications.
I wrote a comments app (yea, I re-invented the wheel), that can be integrated into any other application, with a few lines in the template of the page where comments should be posted (using custom tags).
Say you want a model "thread" to be pluggable into any other model. The idea is to create e generic foreign key (see django documentation on that), and write a small function that takes any object and returns a "thread" corresponding to it (or creates one if necessary), and write a custom template tag that uses that functionality, e.g. {% get_thread for arbitrary_object as thread %}. All posts are related to a thread, which is related to the object, which can be of any type.
You can think of the "thread" object as a kind of a proxy, so instead of having a post be related to a certain "article" or a "blog post", it's just related to a thread, which is abstract in a sense, what is is a thread? It's just a collection of posts. The thread then allows itself to be related to any object regardless of its type. (although it does more than that, it could hold extra information such as allowing/disallowing anon. posts, closing/opening comments on the page, etc ..)
EDIT
Here's how you can create a generic foreign key with the content types framework:
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
class Thread( models.Model ):
object_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
object = generic.GenericForeignKey('object_type', 'object_id')
You can make it more "transparent" by exploiting the implicit "common" interface that django assumes all objects implement ..
#inside the Thread class:
def __unicode__(self):
return unicode(self.object)
def get_absolute_url(self):
return self.object.get_absolute_url()

Your code seems correct. I would keep the post and the comment in a blog app though. I am not saying this is the Django way, but those models are close enough to be in the same app.
How To Divide The Project
I would seperate an app if;
I plan to design it resuable. (and try loose coupling)
(for big projects) It consists of a major section of the project.
On the other hand; having many tiny apps (such as an app with a single model and two views) is hard to read and maintain IMHO.
How Apps Should Interact
This depends on the type of project and the type of the app again. For example if an app is implicitly dependent on another (ie not generic) importing and using references from the other app is acceptable. In this case the second app might be installed alone, but the first one needs the presence of the second.
If you want to make an app highly reusable and generic, such as a commenting app, you might need to integrate some setup mechanism. Maybe some new settings or additional URL configuration, or a special directive/method on your models... django.contrib.admin is a good example for this.
Apps shouldn't interact if it is not necessary though. Designing apps to avoid unnecessary coupling is very useful. It improves your app's flexibility and makes it more maintainable (but possibly with a higher cost in integrating).

Related

Django common comment model to use in different apps

I am creating a project that requires comments in multiple apps like blog, wikis, and pages. I have a commons app that contains all common models. How do I have a Comment model that's common to all apps?
commons/models.py
class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='comments')
body = models.TextField()
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True)
blog/models.py
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
content = models.TextField()
wikis/models.py
class Wiki(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='wikis')
content = models.TextField()
From what I researched, I have the following options,
Three Comment models three apps.
One Comment model in commons app with ForeignKey relations to the other apps (which I believe will cause circular import issues) and end up with a comment table with multiple columns like blog_id, wiki_id, and page_id.
Use Django's GenericForeignKey relationships.
I don't want to do 3. But out of 1 and 2 I would like to know which is the most efficient way to handle this without repeating codes and adding unnecessary database joins.
Or is there a better way to do this?
It's been a while since you asked this question so I'd love to know what conclusions you came to.
One idea would be to create an abstract model in the commons app and then app-specific sub-classes in each app. However, this violates the portability principle since the app will now be dependant upon the commons app.
And therein lies the problem with the idea of a commons app that would contain all the models shared by other apps. While we might consider this to solve the issue of circular references, they are just the symptom of a larger problem caused by ignoring this principle in the first place. Creating further separation only makes things worse.
In your project, you seem to have identified 3 separate web applications and so I think the answer should be to create app-specific models in each one... even if it seems to violate the DRY principle because it favours portability.
In my opinion (which is always subject to change), no app should be dependent upon anything beyond installed modules and Django itself. This ensures portability and thus reusability of code.
I say this knowing that it may result a monolithic application but I feel that this is preferable to circular references caused by cross dependencies. The only solution to that is to spin off core functionality into modules that could be included in any project. The User module included in Django would be a good example of this. Something like django-simple-history would be another. Neither one relies on your code... your code relies upon them.

Django apps , functionalities and models

This is a pretty beginner question, as i have recently learned Django,
I have these questions since i could not find a clear answer for any.
Suppose that we want to build a voting system, and we have users with votes, i have a confusion with the admin user, i should create an app called users? or i should use the users default table by django?
for each app i'll have a model, and for sure database-wise, for example a user will have many votes on many projects, therefore how can i connect these relationships across models in the right way?
Thanks in advance
You don't have to create an app for each model. An app is usually a self-contained, useful entity, that you separate from other apps if you think you'd reuse it in another project in the future. In your case, you could just have one app with various models (Vote, Project).
Use the standard Django User model if you're not going to extend it, but if your project becomes bigger and more features arise, you might want to at least create your own User model subclassing AbstractUser because it's difficult to change it later on. Even if you just define
class User(AbstractUser):
pass
you'll be prepared for the day you want to add a field to your user model.
No, you don't have a model for each app, you can have multiple models per app. To create relationships, you can use ForeignKey and ManyToManyField. If models are tightly related to each other, they belong in the same app. You can have things split in separate apps for the sake of overview, but be careful then with circular references: you can't import Project in the Vote model and Vote in the Project model.
subclass the AbstractUser.
establish relation between various app Models
from other_app.models import Model1
Class Model2(models.Model):
name = models.CharField(max_length=120)
relation = models.ForeignKey(Model1, on_delete=models.DO_NOTHING)
you can import Model of any app where you need it.

What side should a django 'many-to-many' relationship reside on

Lets say I have a recipe website with two basic models, 'User' and 'Recipe'
class User(models.Model):
username= models.CharField()
email = models.CharField()
class Recipe(models.Model):
name = models.CharField()
description = models.CharField()
I would like to add the functionality so that users can 'favorite' a recipe.
In this case, I need to use a many-to-many relationship. My question is, how do I decide which model to add the relationship to?
For example, each user could have a list of 'favorite' recipes:
class User(models.Model):
favorites = models.ManyToManyField(Recipe)
Alternatively, each recipe could have a list of users who Favorited the recipe:
class Recipe(models.Model):
user_favorites = models.ManyToManyField(User)
What is considered the best practice? Is either one better for query performance?
It makes no difference from the database point of view, as pointed out in the comments.
But I have had two arguments where it did matter to me.
First (maybe less important), the built-in admin treats the two models differently by default. The model on which you define the relationship gets a widget for choosing the related objects. And a '+' for conveniently adding new objects of the related type.
secondly, you have to import one of the models in the file of the other one, if they are in different files. This matters if you want to write a reusable app that does not depend on anything outside. It mattered to me also because:
I once (well, not just once actually :)) broke my app/database/etc such, that I decided to start a new project and copy the code there. In this case you have to comment out some settings.INSTALLED_APPS to test step for step that everything works. Here it is important not to have circular includes (to include a commented-out app raises an error). So I try to import the "most basic" into the others, and not the other way round.
This not a simple answer to your question, but two points which I consider. Maybe some more experienced users can correct me if it's wrong in some sense.

Using python builtins to pass django request from middleware to model in order to create default model filters for current user?

I have a fairly complex django application that has been in production for over a year.
The application holds data from different customers. The data is obviously in the same table, separated by customer_id.
Recently the client has started to ask questions about data segregation. Since the app is sold on a per user basis and holds sensitive information, customers have been asking if and how we maintain data segregation per customer, and are there any security measures that we take to prevent data leakages (ie. data from one customer being accessed by another customer).
We do our filters in the view endpoints, but eventually a developer in the team might forget to include a filter in his ORM query, and cause a data leakage
So we came up with the idea to implement default filters on our models. Basically whenever a developer writes:
Some_Model.objects.all()
essentially they will execute:
Some_Model.objects.filter(customer_id = request.user.customer_id)
We plan to achieve this by overriding the objects property on each model to point to a manager with a filtered queryset. Something like this:
class Allowed_Some_Model_Manager(models.Manager):
def get_queryset(self):
return super(Allowed_Some_Model_Manager, self).get_queryset().filter(
customer_id = request.user.customer_id
# the problem is that request.user is not available in models.py
)
class Some_Model(models.Model):
name = models.CharField(max_length=50)
customer = models.ForeignKey(Customer)
objects = Allowed_Some_Model_Manager()
all_objects = models.Manager() # use this if we want all objects
However our problem is that request.user is not available in models.py.
I have found several ways to solve this.
Option 1 includes passing the request.user to the manager each time. However since I am dealing with thousands of lines of old code, I don't want to go and change all of our ORM queries.
Option 2, included using threading.local() to set the request.user in the thread local data.
Something like this: https://djangosnippets.org/snippets/2179/
There is a module that seems to be doing this: https://github.com/Alir3z4/django-crequest
However, a lot of people seem to be against this idea... Namely these two discussions:
django get_current_user() middleware - strange error message which goes away if source code is "changed" , which leads to an automatic server restart
Django custom managers - how do I return only objects created by the logged-in user?
So that brings me to Option 3 which I came up with, and I can not find anybody else using it. Use the python builtins module to pass the user from the middleware to the model.
#middleware.py
import builtins
def process_request(self, request):
if request.user.id:
builtins.django_user = request.user
#models.py
import builtins
class Allowed_Some_Model_Manager(models.Manager):
def get_queryset(self):
if 'django_user' in vars(builtins):
return super(Allowed_Some_Model_Manager, self).get_queryset().filter(
customer_id = django_user.customer_id
)
else:
return super(Allowed_Some_Model_Manager, self).get_queryset()
I have tested the code and it is working on my local django server and on Apache with mod_wsgi. But I really want to hear if there are any pitfalls of this approach. I have never used builtins module before, and I am not sure if I understand how it works, and what is the use-case for it.

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