Django: Accessing Many to Many object through another Many to Many relationship - django

I have simplified my models down a to make it clearer what I am trying to do.
(models.py in app Teams)
from django.db import models
from django.contrib.auth.models import User
import datetime
class Team(models.Model):
users = models.ManyToManyField(User)
team_title = models.CharField(max_length=200)
team_description = models.CharField(max_length=200)
def __unicode__(self):
return self.team_title
(models.py in app Documents)
from django.db import models
import datetime
class Document(models.Model):
teams = models.ManyToManyField("Teams.Team", blank=True)
document_title = models.CharField(max_length=200)
document_description = models.TextField()
def __unicode__(self):
return self.document_title
What I want to achieve is getting a list of users who have are associated with a Document by first getting all the teams associated with the document and then from this getting all the users associated with those teams.
My attempts so far have gone something like this
(view.py in app Documents)
from django.contrib.auth.models import User
from Documents.models import *
from Teams.models import *
def docUsers(request, doc_id):
current_document = Documents.objects.get(pk = doc_id)
associated_users = current_document.teams.all().users
....
Error: 'QuerySet' object has no attribute 'users'
associated_users = current_document.items.all().users.all()
Error: 'QuerySet' object has no attribute 'users'
associated_users = current_document.items.users.all()
Error: 'ManyRelatedManager' object has no attribute 'users'
Am I going about this the wrong way?

Well, yes. current_document.teams.all() is a queryset - more or less, a list - of Teams. It doesn't make sense to ask for current_document.teams.all().users, as a queryset doesn't itself have a 'users' attribute, hence the error. users is an attribute of each Team element within that queryset. So, one way of doing it would be to iterate through the queryset and ask for the users associated with each team.
However, that would be hopelessly inefficient - one database call for each team. A much better way is to ask the database directly: give me all the users who are in teams associated with the current document. Like this:
User.objects.filter(team__documents=current_document)

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

Why do I have to reference the user model this way

I was working on a Django project and I was trying to do something like this to make sure that my model worked no matter what user model is set.
from django.contrib.auth import get_user_model
class Item(models.Model):
title = models.CharField(max_length=100, )
description = models.TextField()
seller = models.ForeignKey(get_user_model())
However when I did this it resulted in errors telling me the user model couldn't be accessed so I had to change it to this
from django.conf import settings
class Item(models.Model):
title = models.CharField(max_length=100, )
description = models.TextField()
seller = models.ForeignKey(settings.AUTH_USER_MODEL)
This works fine but I thought I have done this in the past using the first method. The only difference that time being that I was using a custom user model. They both seem like they are doing the same thing to me so why do I have to use the second method? And does get_user_model() not work with the default user?
This is the source code of the get_user_model() in django:
def get_user_model():
"""
Returns the User model that is active in this project.
"""
from django.db.models import get_model
try:
app_label, model_name = settings.AUTH_USER_MODEL.split('.')
except ValueError:
raise ImproperlyConfigured("AUTH_USER_MODEL must be of the form 'app_label.model_name'")
user_model = get_model(app_label, model_name)
if user_model is None:
raise ImproperlyConfigured("AUTH_USER_MODEL refers to model '%s' that has not been installed" % settings.AUTH_USER_MODEL)
return user_model
As you can see, it pulls the AUTH_USER_MODEL variable from your settings as you do but extracting the app_label and the user class itself. If it does not work you should see one of the two errors in the terminal when this call is done.
I think your answer lies in the Django source. It depends on your setup what happens. Older versions might do it a bit differently.

Trying to create a progress app with Django ContentType and GenericForeignKey

I have an app that I'm calling Progress. I want to have several different apps throughout my project write to it whenever something happens. But all the different apps are not the same in how they broadcast progress. So I thought that a ContentType solution would work.
The only trick that I'm having a hard time figuring out is that I need to write to the Progress app when an event occurs. Such as when a view renders. I've been trying get_or_create but I'm having trouble getting the right configuration in the queryset. Any suggestions for how to correct this?
I want the get_or_create to sit in the view of an app so that the action I want is what writes to the progress.
My Progress Model.py
from django.db import models
from django.template.defaultfilters import slugify
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic
from datetime import datetime
class Progress(models.Model):
"""
The page log. Records the user and the object.
"""
user = models.ForeignKey(User, related_name='user_pagelog')
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
content_object = generic.GenericForeignKey('content_type', 'object_id')
stamp = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = (('user', 'content_type', 'stamp'),)
verbose_name = 'View Log'
verbose_name_plural = 'View Logs'
get_latest_by = 'stamp'
def __str__(self):
return "%s got to %s on %s" % (self.user, self.content_type, self.stamp)
#classmethod
def get_latest_view(cls, user):
"""
Get most recent view log value for a given user.
"""
try:
view_log = cls.objects.filter(user=user).order_by('-stamp')[0]
return view_log.value
except IndexError:
return None
An example of the queryset that I want to write to the Progress app:
Progress.objects.get_or_create(user=request.user, content_type=f.id)
Where f = get_object_or_404(Page, publish=True)
Finally, the error I'm getting:
Cannot assign "1": "Progress.content_type" must be a "ContentType" instance.
Which I think it means the instance isn't getting found? But it exists, so I'm confused.
Thanks.
No, it doesn't mean that at all. It means what it says, that the parameter has to be an instance of the ContentType model, whereas you're passing the ID of the object itself.
You might be able to use content_type along with the actual instance:
Progress.objects.get_or_create(user=request.user, content_object=f)
Otherwise you'll need to get the right ContentType using the get_for_model() method, and pass that along with the object id.

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

Django: NameError 'Business' is not defined

Not sure what is causing this error. Please help
NameError at /dash/
name 'Business' is not defined
Exception Location: /home/src/common/models.py in ImageBank, line 38
Here's the model:
class Business(models.Model):
business_type = models.ManyToManyField(BusinessType)
business_service_type = models.ManyToManyField(ServiceType)
establishment_type = models.ForeignKey(EstablishmentType)
logo = models.ForeignKey(ImageBank)
phone = PhoneNumberField()
address = models.ForeignKey(Address)
website = models.URLField()
name = models.CharField(max_length=64)
def __unicode__(self):
return self.name
The View:
def dashview(request):
coupon = Coupon.objects.filter()
bdnspk = request.user.id
user = request.user.username
bdns = Business.objects.values('name').get(id=bdnspk)
context = {
'coupon':coupon,
'bdns':bdns,
'user':user
}
return render_to_response(
'dash/dash.html',
{},
context,
context_instance = RequestContext(request),
)
EDIT: my models is located in /home/src/common/models.py but my django app is in /home/proj/site/ How do I import that?
ImageBank model:
class ImageBank(models.Model):
business = models.ForeignKey('Business')
image = models.ImageField(upload_to="images/bank")
def url(self):
return self.image.url
Please look at your error: Exception Location: /home/src/common/models.py in ImageBank, line 38 the problem exists in the ImageBank class, which you also seem to be using a ForeignKey reference to in the logo field.
I'm assuming that what the issue is is that you are referencing Business before it is defined as something like a ForeignKey reference inside a field in ImageBank. If this is the case, is ImageBank defined before the Business model inside your models.py? Because doing so will throw this error. The proper way of doing circular ForeignKey references would be to enforce a single ForeignKey with a unique constraint.
Django has this concept built in as a type of field called a OnetoOne field. Have you looked into using a OnetoOne field? See: http://docs.djangoproject.com/en/dev/ref/models/fields/#onetoonefield
Did you import the models in the view? Something like:
from models import Business
at the beginning of the view file
You forgot to import the model in the view, or you're referring to it incorrectly.
If that model is in an app you wrote:
Make sure that the app is listed in INSTALLED_APPS in your settings.py
#settings.py
INSTALLED_APPS = (
'django....',
... more defaults ...,
'myproject.appname',
)
and at the top of your views
#views.py
from appname.models import Business
#or import all models from that app
from appname.models import *
You are making things a lot more complicated on yourself by having your models.py in a strange unrelated location.
Models can only be imported from python modules so you'll need to make sure that your models.py is in a directory that is a python module and that it is on the python path.
You'll be a whole lot better of just putting your models into an app in your project rather than trying to do something like you are.
More or less you're working against the grain and python is a lot nicer if you work with the grain.