Circular import in python+django?! how to make it work? - python

Hello I'm spliting my files because the model is getting bigger. So here we are again with problems:
My models;
If in my Category model I remove the "ArticleToCategory" and the many-to-many relationship it works well. But I need them!
How to fix it?
I deleted the model.py in order to load files from the model package.
Category (models.category):
class Category(MPTTModel):
# relationships
from RubeteDjango01.generic.models.article import Article
from RubeteDjango01.generic.models.article_to_category import ArticleToCategory
articles = m.ManyToManyField(Article, through=ArticleToCategory)
ArticleToCategory (models.article_to_category):
from django.db import models as m
class ArticleToCategory(m.Model):
from RubeteDjango01.generic.models.article import Article
from RubeteDjango01.generic.models.category import Category
article = m.ForeignKey(Article)
category = m.ForeignKey(Category)
class Meta:
db_table = 'articles_to_categories'
verbose_name_plural = 'ArticlesToCategories'
thanks

You can define foreign keys using strings, to avoid exactly this problem.
class Art2C(..):
art = m.ForeignKey('Article')
from_other_app = m.ForeignKey('other_app.Article')

Related

How to make a django model "commentable", "likeable" and "rateable"

I am using Django 2.0.8 and Python 3.5 for a project. I have different models in my project, some of which, I want to allow commenting on - with both the object (e.g. a blogpost) and comments to the blogpost being likeable.
I am using the threaded comments django app to provide commenting functionality.
Assuming I have a model Foo (see below):
from django.db import models
from django.conf import settings
class Foo(models.Model):
owner = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, null=False, default=1, on_delete = models.PROTECT)
# ...
class Likeable():
pass
class Rateable():
pass
How could I use mixins (or any other mechanism for that matter), to make the object Foo "commentable" (i.e. an object which can be commented upon), "likeable" (i.e. an object which can be commented upon) and "rateable" (i.e. an object which can be rated?)- bearing in mind that comments on an objects may be BOTH liked and rated.
According to django documentation , you can achieve this using the Content types Framework. ContentType is a generic model that permits you to track all the models included in INSTALLED_APPS using for that their app_label, model_name and pk. The way it works is easy:
Your generic Comment model
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.conf import settings
class Comment(models.Model):
# Generic relation fields
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
# Model specific fields
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
comment = models.TextField()
created = models.DatetimeField(auto_now_add=True)
# ...
Your reusable generic relation model. The best way is using abstract model classes or mixins. For example, using abstract models:
from django.db import models
from django.contrib.contenttypes.fields import GenericRelation
class Commentable(models.Model):
comments = GenericRelation(Comment)
class Meta:
abstract = True
Your Commentable model:
from django.db import models
class Foo(Commentable, Likeable, ...):
# your stuff
How to use it:
# Add a new comment to Foo
foo = new Foo()
foo.save()
foo.comments.create(author=author, comment="Your comment")
# Retrieve all comments from an specific user no matter the base model
comments = Comment.objects.filter(author=author)
EDIT As #ozren1983 said, each approach has its own downsides, but this is the standard way to do it.
The main advantages are:
You can retrieve all the comments (for example) made in all your commentable models in just one query. Using the approach of having a comment, like, etc table per model, you would need to concatenate a query per model. This could be problematic and a bit challenging if you have a lot of models or if you want to merge the results and order them, for example.
Just one table per functionality (comments, likes) implies just one database migration in case of change. This could be key if your database is huge.
The main disadvantage is the lack of integrity checks of this generic relationship in database. But if you plan to use the django ORM strictly, nothing should be broken.
BONUS: Another approach that many projects use is inheriting the models (one to one relationship) from an specific one called Item or Thread. Then, you can add all the comments, likes, etc functionalities to this model. This is called multi-table inheritance. An example:
from django.db import models
class Thread(models.Model):
pass
class Comment(models.Model):
# Relation with thread
thread = models.ForeignKey(
Thread,
on_delete=models.CASCADE,
related_name="comments"
)
# Model specific fields
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE
)
comment = models.TextField()
created = models.DatetimeField(auto_now_add=True)
# ...
class Foo(Thread):
pass
Unlike using the generic relationships, the main advantage of this method is that, this way, you have database integrity checks.
The main disadvantage is that your database structure could become complex.
Based on my experience and recommendations in Two scoops of Django, I would advise against using GenericForeignKey and GenericRelation. Two big downsides of that approach are:
slow queries
danger of data corruption
Instead, I would use following approach. Let's say you have 3 models:
class User(models.Model):
username = models.CharField(max_length=255)
class Author(models.Model):
name = models.CharField(max_length=255)
class Post(models.Model):
title = models.CharField(max_length=255)
author = models.ForeignKey(Author)
Add abstract Like model, and use it as base class for other models that will implement liking functionality.
class Like(models.Model):
user = models.ForeignKey(User)
date_created = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
class AuthorLike(Like):
author = models.ForeignKey(Author)
class PostLike(Like):
post = models.ForeignKey(Post)
Similarly, add abstract Rating model and use it as a base class:
class Rating(models.Model):
user = models.ForeignKey(User)
rate = models.PositiveSmallIntegerField()
date_created = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
class AuthorRating(Rating):
author = models.ForeignKey(Author)
class PostRating(Rating):
post = models.ForeignKey(Post)
You can use same approach to enable liking and rating to the Comments model you are using:
from threadedcomments.models import ThreadedComment
class ThreadedCommentRating(Rating):
threadedcomment = models.ForeignKey(ThreadedComment)
class ThreadedCommentLike(Like):
threadedcomment = models.ForeignKey(ThreadedComment)
The django-contrib-comments app, according to documentation, makes use of GenericForeignKey, meaning its own model can create a relation to any other model in your project.
A simple solution would be to just copy that existing functionality, creating your own Like/Rate application based on the same concept (i.e. storing the Like/Rate models in that application's models).
I think you would get very far starting out by forking the https://github.com/django/django-contrib-comments codebase.
(I assume you have searched and failed to find an already existing application that already does this).

Django Naturaltime is not working in .annotate

Here I just wants to annotate a field on a model that gives human readable format saying how much time elapsed since it's created
My Model is created 30 seconds ago
My Model Description:
from django.db import models
class MyModel(models.Model):
name = models.CharField(max_length=100)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
#property
def natural_time(self):
return naturaltime(self.created_at)
What I did is here
from django.contrib.humanize.templatetags.humanize import naturaltime
from django.db.models import F
from .models import MyModel
m = MyModel.objects.annotate(timesincecreated=naturaltime(F('created_at'))
print m.values('timesincecreated')
on this print call I am getting the DateTimeField that I used in the model.
But If I want to access the property.
from .models import MyModel
m= MyModel.objects.first()
print m.natural_time
It works.
Any help? TIA.
You cannot use naturaltime function for annotation, annotation is a computation that is done on database level.
Django provides only a set of basic computations which can be processed by the database like Count, Sum, Min, Max, etc. You can refer to official doc to learn more about Query Expressions.

Collecting and data recording about successors of abstract model

My app_templ models definition:
models.py
class TableName(models.Model):
name = models.CharField(max_length=100)
#
class TableAbstract(models.Model):
...
class Meta:
abstract = True
It can be used by other apps:
app1 / models.py
from app_templ.models import TableAbstract
class Table1(TableAbstract):
...
app2 / models.py
from app_templ.models import TableAbstract
class Table2(TableAbstract):
...
and so on...
It is necessary for me that in TableName, names of models (tables) of successors registered.
How to make it by means of coding only in the app_templ app?
Technically, what you are describing sounds fine. You are defining an abstract model and then using it to create several models. You do need to import it, and to specify that you want to create these tables (using your above examples). You should think carefully about why you are using the same model multiple times in different apps (should this actually be one app?), but in theory it is fine.
I don't quite understand your first definition, you should probably define your model something like this:
class TableBaseClass(models.Model):
name = models.CharField(max_length=100)
class Meta:
abstract = True
abstract = True will mean that the model is not created in your database (docs) so for clarity, you could store this file in a location distinct from your regular model classes that create tables.
This code:
from app_templ.models import TableAbstract
class Table1(TableAbstract):
...
should be in models.py in your app

Field diamond pattern in multiple abstract model inheritance in Python/Django

I am having the following model class hierarchy:
from django.db import models
class Entity(models.Model):
createTS = models.DateTimeField(auto_now=False, auto_now_add=True)
class Meta:
abstract = True
class Car(Entity):
pass
class Meta:
abstract = True
class Boat(Entity):
pass
class Amphibious(Boat,Car):
pass
Unfortunately, this does not work with Django:
shop.Amphibious.createTS: (models.E006) The field 'createTS' clashes with the field 'createTS' from model 'shop.boat'.
Even if I declare Boat abstract, it doesn't help:
shop.Amphibious.createTS: (models.E006) The field 'createTS' clashes with the field 'createTS' from model 'shop.amphibious'.
Is it possible to have a model class hierarchy with multiple inheritance and a common base class (models.Model subclass) that declares some fields?
Use this and see if it helps. If you are trying to include the timestamp to the models then just create a base model which includes only the timestamp.
from django.db import models
class Base(models.Model):
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Boat(Base):
boat_fields_here = models.OnlyBoatFields()
class Amphibious(Boat):
# The boat fields will already be added so now just add
# the car fields and that will make this model Amphibious
car_fields_here = models.OnlyCarFields()
I hope this helps. I see that it has been 5 months since you posted this question. If you have already found a better solution then please share it with us, will help us a lot for learning. :)

How to make django admin site to not recognize a foreign key'ed model (on different app) to be a model of current app?

I have three models - two of them are in one app, and the third one is on the another. The structure is like this:
taapp.models:
class Teachers(model.Model):
fullname = models.CharField(max_length=50)
...
class TeachersScale(model.Model):
teacher = models.ForeignKey("Teachers")
abbr = models.ForeignKey("questions.QuestionTypes")
questions.models:
class QuestionTypes(models.Model):
abbr = models.CharField(max_length=5)
......
I registered all these models to admin:
taapp.admin:
from taapp.models import Teachers
from taapp.models import TeachersScale
from django.contrib import admin
from admin_forms import TeachersAdmin, TeachersScaleAdmin
admin.site.register(Teachers, TeachersAdmin)
admin.site.register(TeachersScale, TeachersScaleAdmin)
taapp.admin_forms:
from django import forms
from django.contrib import admin
class TeachersAdmin(admin.ModelAdmin):
list_display = ('fullname', 'email', 'registration_date')
class TeachersScaleAdmin(admin.ModelAdmin):
list_display = ('teacher', 'abbr')
list_filter = ['teacher','abbr']
When I try to add a field to TeachersScale in admin site, I get the following error:
DatabaseError at /admin/taapp/teachersscale/add/
(1146, "Table 'taapp.questions_questiontypes' doesn't exist")
It treats QuestionTypes, as it is a model in taapp. How to solve it? Or is there something wrong with my db design?
I tried TabularInline for QuestionTypes to see if reverse adding works. Well, it works:
questions.admin:
class TeachersScaleInline(admin.TabularInline):
model = TeachersScale
class QuestionTypesAdmin(admin.ModelAdmin):
inlines = [TeachersScaleInline]
Thanks in advance.
It looks like you haven't actually created your questions table, or if you have you've forced it into a different database. Foreign keys expect to share the same database, and it's perfectly standard to have multiple apps sharing the same database. That's why the app name is part of the automatically generated table name.

Categories