How to query database about Model from another App? - django

I have models like this:
class Subscription(models.Model):
visable_name = models.CharField(max_length=50, unique=True)
recipe_name = models.CharField(max_length=50)
website_url = models.URLField()
class User(models.Model):
username = models.CharField(max_length=50)
class UserSubs(models.Model):
subscription = models.ForeignKey(Subscription, to_field='visable_name')
user = models.ForeignKey(User, to_field='username')
And I want to prepare simple ranking, so I came up with something like this: Subscription.objects.annotate(total=models.Count('usersubs')).order_by('-total')
The true problem is that I just discovered that my "simple ranking" should be in another App, where I can't even do from FirstApp import models.Subscription, becouse I get ImportError: cannot import name Subscription.
I actually have no idea how it has to be done.. Maybe should I give up from separate these two Apps?

I still don't really understand why you are trying to split these up, but it seems likely you have a circular dependency: your model files are both trying to import each other. You probably need to remove the import from one side: note that if you're importing simply to use as a reference in defining a ForeignKey, you can use a string: models.ForeignKey('FirstApp.Subscription') instead of the actual class.

Related

How do I work around 'circular import' in Django?

I'm getting a 'circular import' error when trying to makemigrations in Django. The two models in question are these. The error is being flagged on Team.
from django.db import models
from django.contrib.auth.models import User
from footballapi.models.team import Team
from footballapi.models.bio import Bio
class Player(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.ForeignKey(Bio, on_delete=models.CASCADE)
teams = models.ManyToManyField(Team, on_delete=models.CASCADE, related_name="members")
from django.db import models
from footballapi.models.player import Player
class Team(models.Model):
name = models.CharField(max_length=50)
roster_spot = models.ForeignKey(Player, on_delete=models.CASCADE)
I think the issue is with the ManyToManyField, and I keep reading that I should use a string instead of the import. But I've tried every combination of words and can't find the right string. What should it be? By the way, these models are all from the same app.
Besides the classes you can also use a string "app_name.model_name" for a foreignkey relationship thus avoiding to import each other.
teams = models.ManyToManyField("app_name.Team", on_delete=models.CASCADE, related_name="members")
Alternatively, you could change your datamodel with a through table in your m2m relationship. Using this table you could set a boolean indicating if a player is playing roster_post. Check out the docs: https://docs.djangoproject.com/en/4.1/ref/models/fields/#django.db.models.ManyToManyField.through

Report building in Django

I am trying to build a system which will enable my users to create their own reports based on the model of their choosing without me having to code them every time they need updating.
In order to do this, I've come up with the following models:-
from django.db import models
from django.contrib.contenttypes.models import ContentType
class ReportField(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
data_method = models.CharField(max_length=100)
def get_value_for(self, object):
return getattr(object, self.data_method)
class Report(models.Model):
name = models.CharField(max_length=200)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
data_fields = models.ManyToManyField(ReportField)
The idea is that users can create a Report based on the model they're interested in. They can then add any number of ReportFields to that report and, when the report runs, it will call the data_method (the name of a property) on each instance of the model in the db.
The bit I'm having trouble with is defining which properties the users can have access to. I need to have a way of creating a load of ReportFields with certain data_methods for each model. But I don't want to create them by hand - I want it to work in a similar way to the way Permissions work in Django, if that's possible, like this:-
class MyModel(models.Model):
class Meta:
data_methods = (
('property_name_1', 'Property Name 1'),
('property_name_2', 'Property Name 2'),
etc.
)
From reading the source code, Django seems to run a management command after every migration on that model to make sure the model permissions are created. Is that the only way to do this? Am I going in the right direction here, or is there a better way?

django rest framework accessing and editing nested model

Let's say I've got 2 models
class Person(models.Model):
name = models.CharField(max_length=50)
class Language(models.Model):
person = models.ForeignKey(
Person, related_name='prs', on_delete=models.CASCADE)
name = models.CharField(max_length=50)
I want to be able to access all persons languages like that -> person/{person_id}/language
and to access and edit specific language like that -> person/{person_id}/language/{language_id}
In this scenario, you can use drf-nested-routers package.
As you are new to DRF, I know there is chance that you haven't seen routers and ModelViewSet yet, so I would recommend you to go through that first. Here is the link which explains this flow.

Django Relation between two models

I am very new to Django.
Can you please give a boilerplate of models how to relate two models between each other.
--Below is Section model
from articles.models import Article
# Create your models here.
class Section(models.Model):
#associations
user = models.ForeignKey(settings.AUTH_USER_MODEL)
article = models.ForeignKey(Article) #Article
--Below is Article model
from sections.models import Section
User = settings.AUTH_USER_MODEL
# Create your models here.
class Article(models.Model):
owner =models.ForeignKey(User, null=False)
sections = models.ManyToManyField( Section )
However. I got the below error:
ValueError: Cannot create form field for 'article' yet, because its related model 'articles.models' has not been loaded yet
Thanks All
B
Breaking cyclic imports
You defined a cyclic import: one module first has to import the other module, but the other module fist has to implement that module, so you defined a cycle.
In Django, one does not per se has to use a class reference to make ForeignKeys, one can use strings that refer to the correct model. In that case the Django framework, will later resolve these.
So we can break the cycle, for example with:
# sections/models.py
# no import from articles
# Create your models here.
class Section(models.Model):
#associations
user = models.ForeignKey(settings.AUTH_USER_MODEL)
# we use a string literal
article = models.ForeignKey('articles.Article', on_delete=models.CASCADE)
and then in the articles/models.py:
# articles/models.py
from sections.models import Section
User = settings.AUTH_USER_MODEL
# Create your models here.
class Article(models.Model):
owner = models.ForeignKey(User, null=False)
sections = models.ManyToManyField(Section)
So here we no longer import articles/models.py in the sections/models.py, and thus we break the cyclic import.
Note that you need to specify an on_delete for a ForeignKey, for example models.CASCADE.
Django's reverse relations
For this specific application however, it seems that you make a double relation between Section and Article, that basically is one relation, you should not do that, Django automatically writes the reverse relation, what you probably want to do, is give it a proper name, for example:
# sections/models.py
# no import from articles
# Create your models here.
class Section(models.Model):
#associations
user = models.ForeignKey(settings.AUTH_USER_MODEL)
# we use a string literal
article = models.ForeignKey(
'articles.Article',
on_delete=models.CASCADE,
related_name='sections'
)
and for articles/models.py:
# articles/models.py
User = settings.AUTH_USER_MODEL
# Create your models here.
class Article(models.Model):
owner = models.ForeignKey(User, null=False)
# no relation to section
Here we can obtain all Sections that relate to some_article with some_article.sections.all().

Django - "last_modified" or "auto_now_add" for an App (or more than one Model?)

I know Django has a feature of last_modified field (models.DateTimeField(auto_now_add=True)
)..
but let's say I have a certain App, and I want to know when was the last change for any of its Model (I don't really care which model was changed, I just want to know when was the latest change for this app..)
do I really have to write a last_modified field for each model (I have 9 of them for the moment...), and then check for each of them which is the latest?
any help will be appreciated :)
Thanks
You could create a base class that defines the last_modified field...
class YourBaseClassName(models.Model):
last_modified = models.DateTimeField(auto_now=True)
and then inherit from that
class AnotherClass(YourBaseClassName):
another_field = models.CharField(max_length=50)
In The End I made a table for constants for my app (actually I had it before for use of other things).
so the Table looks like this:
from django.db import models
from django.db.models.signals import post_save
class Constant(models.Model):
name = models.CharField(max_length=50)
value = models.CharField(max_length=50)
and added a consant named "version_date".
Than, I added this code to the bottom of my models.py, to track all changes in all the models in the app.
myapp = models.get_app('myapp')
models2track = models.get_models(myapp)
def update_version(sender, **kwargs):
for model in models2track:
post_save.disconnect(update_version, sender=model, dispatch_uid="some_uid"+model._meta.db_table)
version_date = Constant.objects.get_or_create(id=1,name="version date")[0]
version_date.value = str(int(time.time()))
version_date.save()
for model in models2track:
post_save.connect(update_version, sender=model, dispatch_uid="some_uid"+model._meta.db_table)
for model in models2track:
post_save.connect(update_version, sender=model, dispatch_uid="some_uid"+model._meta.db_table)
This way, I don't need to change my DB Schema.. only need to add the code mentioned.
thanks all