Django's DB API can't come back with all results - django

I'm trying to list all Characters from a User, but my code only return the first Character, can someone lend me a hand?
I'm using the User class from django.contrib.auth.models package.
models.py
class Character(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=20)
def __str__(self):
return self.name
views.py
def sheetList(request):
charL = get_list_or_404(Character, pk=request.user.id)
return render(request, 'sm/sheetList.html',{'charList': charL})

You're filtering on the primary key, which by definition is unique. I suspect you meant to filter on the user:
get_list_or_404(Character, user=request.user)

Related

Filter queryset only where the user is added to ManyToManyFIeld

I want to return all leads that contain a current user. How to do it?
Models.py
class CustomUser(AbstractUser):
def __str__(self):
return self.email
class Lead(models.Model):
budget = models.IntegerField()
buyer = models.ManyToManyField(CustomUser)
What queryset is expected
Lead.objects.filter(buyer__have=request.user)
You can filter with:
Lead.objects.filter(buyer=request.user)
this will make an INNER JOIN, and return only Lead objects where request.user is (one of) the buyers.

Querying a User Profile model

I'm having trouble with this query.
Users application
models.py
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
current_story = models.PositiveSmallIntegerField(null=True)
return f'{self.user.username} Profile'
To avoid confusion, the 'current_story' will eventually be a foreign key for the Books.story model (excluded here) once I learn how to do use a foreign key across apps.
Books application
models.py
class character(models.Model):
fk_user = models.ForeignKey(User, default='1', on_delete=models.CASCADE)
fk_story = models.ForeignKey(story, default='1', on_delete=models.CASCADE)
name = models.CharField(max_length=100, default='', null=True)
def __str__(self):
return self.name
class Meta:
ordering = ('name', )
views.py
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from users.models import Profile
from .models import (character)
class listof_characters(LoginRequiredMixin, ListView):
model = character
template_name = '/list_characters.html'
context_object_name = 'characters'
ordering = ['name']
def get_queryset(self):
?
This is where i'm stuck. I need character.fk_story = User.profile.current_story.
I don't how to phrase this query, I've tried several different things based on other answers and I've tried User.current_story, User.userprofile.current_story as well.
I just need to filter the list of characters by current user's 'current_story' value.
Try to use this:
fk_story = Profile.objects.filter(owner=request.user).order_by('current_story')
Do what Lars said but use self.request.user instead of request.user
UPDATE
urls.py
urlpatterns = [
# other urls
path('characters/<int:pk>/', views.CharactersView.as_view(), name='characters'),
]
views.py
class CharactersView(LoginRequiredMixin, DetailView)
model = story
template_name = '/list_characters.html'
def get_queryset(self):
story = story.objects.get(pk=self.kwargs['pk']) # you need to pass a pk of the story you need the characters for in your url
characters = character.objects.filter(fk_story=story)
return characters
(here i replaced ListView with DetailView so we can query by the story model)
then in your template:
{% for character in characters %}
{{ character.name }}
{% endfor %}
P.S. Always start your Model names and ClassBasedViews with uppercase letters for better readability
def get_queryset(self):
profile = Profile.objects.get(user=self.request.user)
# or better use
# profile = get_object_or_404(Profile, user=self.request.user)
queryset = character.objects.filter(fk_story =profile.current_story)
return queryset
In order for this to work, you also need to change your Profile model.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
current_story = models.ForeignKey(story, on_delete=models.CASCADE)
return f'{self.user.username} Profile'
Note here I am jusing foreignkey rather than integerfield.

How to filter a Django model based on requirement from another model?

I'd like to retrieve all my contacts from my Contact model excluding those listed on my DoNotContact model. Is the following the most efficient way to do that: contacts = Contact.objects.filter(dont_contact=False) Wondering if this is going to take long to process, is there a more efficient way?
class Contact(models.Model):
email = models.CharField(max_length=12)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
audience = models.ForeignKey(Audience, on_delete=models.CASCADE)
def dont_contact(self):
try:
get_object_or_404(DoNotContact, email=self.email)
return True
except:
return False
def __str__(self):
return self.email
class DoNotContact(models.Model):
email = models.CharField(max_length=12)
#views.py
def send_email(request):
if request.method == "POST":
contacts = Contact.objects.filter(dont_contact=False)
Kwargs used model queryset filter methods are resolved into database columns. dont_contact here is a method and doesn't exist as a column in Contact model so calling Contact.objects.filter(dont_contact=False) will raise a FieldError.
For current implementation of your models you can do following
dont_contacts = DoNotContact.objects.values('email')
contacts = Contact.objects.exclude(email__in=dont_contacts)
A better solution with higher performance is to remove DoNotContact and add a BooleanField to Contact which handles your requirement.

Django admin site for limited privilege users

I have a Django application, and have two apps within the project: accounts and store. In my store app, I have Product, Category model and other models.
I have admin site, from where I can register Products. In the accounts, I allow general people to signup and log in.
Now, I also want users to be able to register Products, so that they can sell their own products. However, I do not want to give them to a complete access to the admin site.
How can I give such limited access to the admin site to general users?
Thanks.
In admin you can make groups and assign access to models you wanted and same could be applied to users but you might be interested in limited access to models records to which logged user have itself added. in order to achieve this you have to define one column in model to foreign key to users like below I have defined model for company and assigned each company to user:
class Company(models.Model):
name = models.CharField(max_length=64, primary_key=True)
kam = models.ForeignKey(User, verbose_name='KAM', blank=True, null=True)
address = models.TextField(blank=True, null=True)
city = models.CharField(max_length=32, blank=True, null=True)
country = models.CharField(max_length=32, blank=True, null=True)
phone = models.CharField(max_length=32, blank=True, null=True)
fax = models.CharField(max_length=32, blank=True, null=True)
url = models.URLField(blank=True, null=True)
class Meta:
verbose_name_plural = 'Companies'
#unique_together = (('name', 'kam'),).
def __unicode__(self):
return self.name
Now your model will be associated with user, Now you could restrict records to be loaded according to logged user using admin.py in modeladmin definition like given below:
def queryset(self, request):
qs = super(CompanyAdmin, self).queryset(request)
# If super-user, show all comments
if request.user.is_superuser:
return qs
return qs.filter(kam=request.user)
thats simple let me know if this is what you want?
Also you could assign read only right. in model admin
Admin uses a class ModelAdmin to render the page as you would probably already know. That class has a queryset method which you override based with a new filter, based on who is accessing the site, as suggested by sharafjaffri.
But that filtering alone isn't sufficient. You also need to filter the values displayed in the dropdowns, to only those created by the user. And then when saved, you should associate the new object with the portal of the user adding it.
Here is my quick untested implementation of the same:
class PortalAdmin(admin.ModelAdmin):
exclude = ('portal',)
def queryset(self, request):
"""
Filter the objects displayed in the change_list to only
display those for the currently signed in user.
"""
qs = super(UserAdmin, self).queryset(request)
if request.user.is_superuser:
return qs
else:
return qs.filter(portal=request.user.profile.portal)
def formfield_for_foreignkey(self, db_field, request=None, **kwargs):
the_model = db_field.related.parent_model
if hasattr(the_model,'portal'):
kwargs['queryset'] = the_model.objects.filter(portal=request.portal)
return super(PortalAdmin,self).formfield_for_foreignkey(db_field, request, **kwargs)
def save_model(self, request, obj, form, change):
if not change:
obj.portal = request.portal
obj.save()

"Foreign Keys" across very separate databases in Django

I've writing a Django site that uses two different databases. One is the local, let's call it, "Django", database that stores all of the standard tables from a pretty standard install -- auth, sites, comments, etc. -- plus a few extra tables.
Most of the data, including users, comes from a database on another server, let's call it the "Legacy" database.
I'm looking for suggestions on clean, pythonic ways of connecting the two databases, particularly in regards to users.
I'm using a proxy model, which works great when I can explicitly use it, but I run into problems when I'm accessing the user object as a related object (for example, when using the built-in django comments system).
Here's what the code looks like:
models.py: (points to the Django database)
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User as AuthUser, UserManager as AuthUserManager, AnonymousUser as AuthAnonymousUser
class UserPerson(models.Model):
user = models.OneToOneField(AuthUser, related_name="person")
person_id = models.PositiveIntegerField(verbose_name='Legacy ID')
def __unicode__(self):
return "%s" % self.get_person()
def get_person(self):
if not hasattr(self, '_person'):
from legacy_models import Person
from utils import get_person_model
Person = get_person_model() or Person
self._person = Person.objects.get(pk=self.person_id)
return self._person
person=property(get_person)
class UserManager(AuthUserManager):
def get_for_id(self, id):
return self.get(person__person_id=id)
def get_for_email(self, email):
try:
person = Person.objects.get(email=email)
return self.get_for_id(person.pk)
except Person.DoesNotExist:
return User.DoesNotExist
def create_user(self, username, email, password=None, *args, **kwargs):
user = super(UserManager,self).create_user(username, email, password, *args, **kwargs)
try:
person_id = Person.objects.get(email=email).pk
userperson, created = UserPerson.objects.get_or_create(user=user, person_id=person_id)
except Person.DoesNotExist:
pass
return user
class AnonymousUser(AuthAnonymousUser):
class Meta:
proxy = True
class User(AuthUser):
class Meta:
proxy=True
def get_profile(self):
"""
Returns the Person record from the legacy database
"""
if not hasattr(self, '_profile_cache'):
self._profile_cache = UserPerson.objects.get(user=self).person
return self._profile_cache
objects = UserManager()
legacy_models.py: (points to the "Legacy" database)
class Person(models.Model):
id = models.AutoField(primary_key=True, db_column='PeopleID') # Field name made lowercase.
code = models.CharField(max_length=40, blank=True, db_column="person_code", unique=True)
first_name = models.CharField(max_length=50, db_column='firstName', blank=True) # Field name made lowercase.
last_name = models.CharField(max_length=50, db_column='lastName', blank=True) # Field name made lowercase.
email = models.CharField(max_length=255, blank=True)
def __unicode__(self):
return "%s %s" % (self.first_name, self.last_name)
def get_user(self):
from models import User
if not hasattr(self,'_user'):
self._user = User.objects.get_for_id(self.pk)
return self._user
user = property(get_user)
class Meta:
db_table = u'People'
I've also whipped up my own middleware, so request.user is the proxy User object also.
The real problem is when I'm using something that has user as a related object, particularly in a template where I have even less control.
In the template:
{{ request.user.get_profile }}
{# this works and returns the related Person object for the user #}
{% for comment in comments %} {# retrieved using the built-in comments app %}
{{ comment.user.get_profile }}
{# this throws an error because AUTH_PROFILE_MODULE is not defined by design #}
{% endfor %}
Short of creating a wrapped version of the comments system which uses my proxy User model instead, is there anything else I can do?
Here's how I resolved it. I stopped using the User proxy altogether.
models.py:
from django.db import models
from legacy_models import Person
from django.contrib.auth.models import User
class UserPerson(models.Model):
user = models.OneToOneField(User, related_name="person")
person_id = models.PositiveIntegerField(verbose_name='PeopleID', help_text='ID in the Legacy Login system.')
def __unicode__(self):
return "%s" % self.get_person()
def get_person(self):
if not hasattr(self, '_person'):
self._person = Person.objects.get(pk=self.person_id)
return self._person
person=property(get_person)
class LegacyPersonQuerySet(models.query.QuerySet):
def get(self, *args, **kwargs):
person_id = UserPerson.objects.get(*args, **kwargs).person_id
return LegacyPerson.objects.get(pk=person_id)
class LegacyPersonManager(models.Manager):
def get_query_set(self, *args, **kwargs):
return LegacyPersonQuerySet(*args, **kwargs)
class LegacyPerson(Person):
objects = LegacyPersonManager()
class Meta:
proxy=True
and legacy_models.py:
class Person(models.Model):
id = models.AutoField(primary_key=True, db_column='PeopleID') # Field name made lowercase.
code = models.CharField(max_length=40, blank=True, db_column="person_code", unique=True)
first_name = models.CharField(max_length=50, db_column='firstName', blank=True) # Field name made lowercase.
last_name = models.CharField(max_length=50, db_column='lastName', blank=True) # Field name made lowercase.
email = models.CharField(max_length=255, blank=True)
def __unicode__(self):
return "%s %s" % (self.first_name, self.last_name)
def get_user(self):
from models import User
if not hasattr(self,'_user'):
self._user = User.objects.get_for_id(self.pk)
return self._user
def set_user(self, user=None):
self._user=user
user = property(get_user, set_user)
class Meta:
db_table = u'People'
Finally, in settings.py:
AUTH_PROFILE_MODULE = 'myauth.LegacyPerson'
This is a simpler solution, but at least it works! It does mean that whenever I want the legacy record I have to call user_profile, and it means that there's an additional query for each user record, but this is a fair trade-off because actually it isn't very likely that I will be doing a cross check that often.