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.
Related
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.
I need to dynamically create a series of models and relationships in a Postgres database for the purpose of demoing some features.
The issue is these models can't interfere with or be returned by queries in the normal functioning of the Django app. However they must have the full behavior set of the model and the ability to be returned by queries during the demo.
Using factories (we use Factory Boy primarily) is imperfect because the models can't be queried without being saved to the database at which point they interact with regular operation queries.
A second database is imperfect as well because the demo occurs during normal app operation and thus the "valid" model instances still need to be available.
Flagging the models as is_demo also won't work as there's a half dozen different types of models that need to be instantiated for the demo to work as intended.
I could clear the specific models that are created with a cron job or similar but they'd effect the database between creation and the scheduled job.
Running everything in a staging environment is acceptable, but the ideal answer allows me to run the demos on the production build.
Is there an accepted approach or useful library that I've missed?
A sample model that's involved in the demo:
class Transaction(models.Model):
amount = models.DecimalField(max_digits=8, decimal_places=2, db_index=True,
editable=False)
assigned_user = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateField(db_index=True, editable=False)
description = models.CharField(max_length=255, null=True, blank=True)
is_declined = models.BooleanField(default=False)
is_pending = models.BooleanField(db_index=True)
fi_account = models.ForeignKey(
FinancialAccount, on_delete=models.CASCADE)
#property
def organization(self):
org = self.fi_account.organization
return org
We'd need to be able to query this model and use its .organization property successfully. Without having it effect other queries.
You could create a clone of your production environment (database and django app), then update your production codebase to include the demo features/fixes. This way you'd have a copy of the "valid" production models in addition to the new models you're demoing.
It sounds like you should have a "staging" environment that closely matches your production environment for situations just like this.
Edit
One option I just thought of would be to create a Demo model which keeps track of records created as part of a demo. This Demo model would be have a ForeignKey to your User model, so each User can create/destroy their own demo (or your view can create it).
This would let you delete any records created as part of a specified Demo object.
However, you'd have to make sure you add an exclude for all of your current queries/filters of the models you have now.
Honestly, I'm not sure if what you're asking for can be done without exhaustively modifying all of your Model filters to exclude whatever demo records you create.
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.
I am developing a new project from scratch with Django. I see that there are many apps for handling translation of dynamic content.
Django-multilingual
Django-pluggable-model-i18n
Django-modeltranslation
Transdb
Django-multilingual-model-
Django-transmeta
to name few.
Transdb, transmeta and multilingual sounded fair, but I want to read some personal experiences.
Which one of them should I choose?
I agree with S.Lott in that you will want to think about what you need from internationalization. However, this is a very good run-down of all of the mentioned packages:
http://blog.muhuk.com/2010/01/06/dynamic-translation-apps-for-django.htm
My personal pick thus far is "none of the above". I am currently prototyping with datatrans:
http://github.com/citylive/django-datatrans
http://www.botondus.com/dynamic-translation-with-django-datatrans/
But I may yet choose another solution.
There are 2 kinds of model translation:
Adding extra columns/fields to the model to translate
Using a second table for translations
The 1st approach is more efficient because you don't use extra joins. And the 2nd one is less intrusive because it doesn't modify the original model table. I personally like the 1st option better and that's one of the main reasons why I use django-modeltranslation.
You can also have a look to this comparison grid.
Django-nani is a brand-new application, which is fast, and well-written using probably the best design approach.
It's still in development but works well and has a very complete documentation.
This is a snippet to see how the multilingual models are defined:
from django.db import models
from nani.models import TranslatableModel, TranslatedFields
class DjangoApplication(TranslatableModel):
name = models.CharField(max_length=255, unique=True)
author = models.CharField(max_length=255)
translations = TranslatedFields(
description = models.TextField(),
description_author = models.CharField(max_length=255),
)
def __unicode__(self):
return self.name
I'm using the django i18n for a bilingual project: I'm really really satisfied of this one and I definitely would recommend it, but I've got to say that I've never tried one of the others...
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).