Django __unicode__ extended Auth_User model - django

I'm extending a the auth.models.User but I'm having troubles to implement the __unicode__ methode.
from django.db import models
from django.contrib.auth.models import User
class Artist(models.Model):
user = models.OneToOneField(User)
city = models.CharField(max_length=30)
bio = models.CharField(max_length=500)
Now how to I access the user fields as I want to return the name for Django Admin.

class Artist(models.Model):
# fields
def __unicode__(self):
return "{first_name} {last_name}".format(
**dict(
first_name=self.user.first_name,
last_name=self.user.last_name
)
)
Though User already has a function to concat the name fields, so this will work too:
def __unicode__(self):
return "{0}".format(self.user.get_full_name())
Or even
def __unicode__(self):
return self.user.get_full_name()

Related

Django: Linking Models in different apps gives circular import error

I have two apps in my project names quiz_app_teacher and accounts
many models from these files are connected with each other, but when i try to migrate I get this error
File "F:\self\quiz_site\quiz_app_teacher\models.py", line 2, in
from accounts import models as account_models File "F:\self\quiz_site\accounts\models.py", line 13, in
class Student(models.Model): File "F:\self\quiz_site\accounts\models.py", line 15, in Student
quizzes = models.ManyToManyField(quiz_models.Quiz) AttributeError: partially initialized module 'quiz_app_teacher.models' has no
attribute 'Quiz' (most likely due to a circular import)
quiz_app_teacher/models.py
from django.utils import timezone
from accounts import models as account_models
from django.db import models
# Create your models here.
ANSWER_CHOICES = (
('A', 'A'),
('B', 'B'),
('C','C'),
('D','D'),
)
class Quiz(models.Model):
#https://www.sankalpjonna.com/learn-django/the-right-way-to-use-a-manytomanyfield-in-django
name=models.CharField(max_length=250)
quiz_id = models.CharField(max_length=300,)
created_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(account_models.User, on_delete=models.CASCADE,related_name='quizzes')
#Using related_names Author.quizzes.all()
#will list all the quizzes which are made by that author.
course = models.ForeignKey(account_models.Course, on_delete=models.CASCADE, related_name='quizzes')
def save(self, *args, **kwargs):
#override default save method to do something before saving object of model
if not self.quiz_id:
self.quiz_id = self.name+"-"+self.created_date.strftime("%M%S") #TODO:Edit this
super(Quiz, self).save(*args, **kwargs)
def __str__(self):
return self.name
class result(models.Model):
#quiz=models.OneToOneField(Quiz,on_delete=models.CASCADE)
student=models.ForeignKey(account_models.Student , on_delete=models.CASCADE,related_name='my_results')#maybe use account_models.User
quiz=models.ForeignKey(Quiz, on_delete=models.CASCADE, related_name='results')
points=models.IntegerField()
def __str__(self):
return f"Student name: { str(self.student)} Points:{ str(self.points)}"
class Question(models.Model):
quiz = models.ForeignKey(Quiz, on_delete=models.CASCADE, related_name='questions')
#quiz=models.ForeignKey(Quiz, on_delete=models.CASCADE)
question=models.CharField(max_length=300,)
A = models.CharField(max_length=200,)
B = models.CharField(max_length=200,)
C = models.CharField(max_length=200,)
D = models.CharField(max_length=200,)
answer = models.CharField(max_length=200,choices=ANSWER_CHOICES,default='A')
question_number=models.IntegerField()
def __str__(self):
return self.question
accounts/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
import quiz_app_teacher.models as quiz_models
# Create your models here.
class Course(models.Model):
name = models.CharField(max_length=30)
Year=models.IntegerField()
def __str__(self):
return self.name
class User(AbstractUser):
is_teacher = models.BooleanField(default=False)
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
quizzes = models.ManyToManyField(quiz_models.Quiz)
course = models.ForeignKey(Course,on_delete=models.CASCADE, related_name='class_students')
def __str__(self):
return self.user.username
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
quizzes = models.ManyToManyField(quiz_models.Quiz)
def __str__(self):
return self.user.username
If i try to define relations using
quizzes = models.ManyToManyField(to='quiz_models.Quiz')
SystemCheckError: System check identified some issues:
ERRORS: accounts.Student.quizzes: (fields.E300) Field defines a
relation with model 'quiz_models.Quiz', which is either not installed,
or is abstract. accounts.Student.quizzes: (fields.E307) The field
accounts.Student.quizzes was declared with a lazy reference to
'quiz_models.quiz', but app 'quiz_models' isn't installed.
accounts.Student_quizzes.quiz: (fields.E307) The field
accounts.Student_quizzes.quiz was declared with a lazy reference to
quiz_models.quiz', but app 'quiz_models' isn't installed.
You can remove your imports and refer to your ForeignKey models like so:
models.ForeignKey('account_models.Course', ...)
This should allow you to run your migrations without a circular import
https://docs.djangoproject.com/en/4.0/ref/models/fields/#django.db.models.ForeignKey
Edit from OP:
Update: I fixed it by running migrations for both files simultaneously. python manage.py makemigrations accounts quiz_app_teacher

How do I best restrict by user and by data model using Django?

I'm using django-guardian and I encountered some issues with the default mixins. And I want to know if there's a better way to do this.
GitHub Link: https://github.com/iaggocapitanio1/django_homepage
Problem:
If I want to limit access at both the model and object levels, using these two mixins (PermissionRequiredMixin, PermissionListMixin) is not a very easy task. Because the permissions_required attribute is overridden. To get around this I had to create a new attribute "object_permission" and do the following:
Model Looks like:
# Create your models here.
from django.db import models
from localflavor.br import models as localModels
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
class Customer(models.Model):
user: User = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.user.first_name} {self.user.last_name}'
class Company(models.Model):
user: User = models.OneToOneField(User, on_delete=models.CASCADE)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='comapnies')
def __str__(self):
return f'{self.user.first_name} {self.user.last_name}'
class Project(models.Model):
name = models.CharField(max_length=100)
owner = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='projects')
class Meta:
permissions = (('read_project', 'Read Project'),)
def __str__(self):
return self.name
class House(models.Model):
rooms = models.IntegerField()
postal_code = localModels.BRPostalCodeField()
project = models.ForeignKey(Project, on_delete=models.CASCADE)
Here I needed to create a new attribute ("object_permission") to limit object-level access
in the View:
class ProjectsListView(PermissionRequiredMixin, PermissionListMixin, ListView):
template_name = 'home/projects.html'
model = models.Project
permission_required = ["homepage.view_project"]
object_permission = ["read_project"]
redirect_field_name = 'next'
login_url = 'login/'
get_objects_for_user_extra_kwargs = {}
def get_object_permission(self, request: HttpRequest = None) -> List[str]:
if isinstance(self.object_permission, str):
perms = [self.object_permission]
elif isinstance(self.object_permission, Iterable):
perms = [p for p in self.object_permission]
else:
raise ImproperlyConfigured("'PermissionRequiredMixin' requires "
"'permission_required' attribute to be set to "
"'<app_label>.<permission codename>' but is set to '%s' instead"
% self.permission_required)
return perms
def get_get_objects_for_user_kwargs(self, queryset):
return dict(user=self.request.user,
perms=self.get_object_permission(self.request),
klass=queryset,
**self.get_objects_for_user_extra_kwargs)
#receiver(post_save, sender=models.Project)
def project_post_save(sender, **kwargs):
"""
Create a Profile instance for all newly created User instances. We only
run on user creation to avoid having to check for existence on each call
to User.save.
"""
project: models.Project = kwargs["instance"]
created: bool = kwargs["created"]
if created:
user = models.User.objects.get(pk=project.owner.user.id)
assign_perm("read_project", user, project)
Am I using the right approach to filter data relative to each user? How do I combine both the page access limitation and the relative data of each user in a class model view?

Displaying foreign key in django admin panel

I cant seem to find the correct answer to display 1 foreign key value:
it displays "house label" in the correct "table" it displays the value. but it does not give the perfect "column" name. i would love to display "house_name" in the table.. any idea's?
admin.py
from django.contrib import admin
from .models import UserProfile,House
# Register your models here.
class UserProfileAdmin(admin.ModelAdmin):
def house_label(self, obj):
return obj.house.house_name
list_display = ('user', 'api_key','house_label')
admin.site.register(UserProfile,UserProfileAdmin)
admin.site.register(House)
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class House(models.Model):
house_name = models.CharField(max_length=500,blank=False, null = False)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name ="profile")
api_key = models.CharField(max_length=500, blank=False, null = False)
house = models.ForeignKey(House, on_delete=models.CASCADE, related_name = "house")
If you just want it to say house and display the name, you need a __str__ method on your House model that looks like this:
class House(models.Model):
house_name = models.CharField(max_length=500,blank=False, null = False)
def __str__(self):
return f"{self.house_name}"
and your admin class would be:
class UserProfileAdmin(admin.ModelAdmin):
list_display = ('user', 'api_key', 'house')
If you truly want it to say house_name you just need to rename your admin function and refer to it by that name in your list_display.
class UserProfileAdmin(admin.ModelAdmin):
def house_name(self, obj):
return obj.house.house_name
list_display = ('user', 'api_key','house_name')

how to chain field?

This my engine models
from django.db import models
class Colors(models.Model):
color_name = models.CharField(max_length=50)
def __unicode__(self):
return self.color_name
This my Cars models
from django.db import models
class Cars(models.Model):
car_model = models.CharField(max_length=50)
car_colors = models.ManytoManyField(Colors, related_name='Car Colors')
def __unicode__(self):
return self.car_model
O.K. Let's see my CarsData Model.
This my CarsData models
from django.db import models
class CarsData(models.Model):
car_barcode= models.CharField(max_length=50)
available_color = ChainedForeignKey(
Cars,
chained_field="car_model",
chained_model_field="car_colors",
show_all=False,
auto_choose=True
)
def __unicode__(self):
return self.car_barcode
My admin.py looks like that:
from django.contrib import admin
from django import forms
from myapp.models import *
class CarsDataAdminForm(forms.ModelForm):
class Meta:
model = CarsData
def __init__(self, *arg, **kwargs):
super(CarsDataAdminForm, self).__init__(*arg, **kwargs)
self.fields['available_color'].choices = [(csc.id,csc.car_colors) for csc in Cars.objects.all()
class CarsDataAdmin(admin.ModelAdmin):
form = CarsDataAdminForm
admin.site.register(CarsData,CarsDataAdmin)
Is there anyway to show in the ChoiceField 'just' color_name field datas? I see just car_model because i have to set it :
def __unicode__(self):
return self.car_model
How can i chain available_colors field to color_name field? I want to show in available_colors choices just color names like red, blue, black, white...
Can you please give me an example?
I think I don't get your code:
class CarsData(models.Model):
car_barcode = models.CharField(max_length=50)
available_color = ChainedForeignKey(
Cars,
chained_field="car_model", # should be a field in THIS model
chained_model_field="car_colors", # should be the matching field
# in the Cars model
show_all=False,
auto_choose=True
)
And both referenced fields must be ForeignKeys to the same (third) model.
Maybe this works, even without changing the AdminForm (I also changed the model names to singular and remove some repetitions, as the most common use dictates):
class Color(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
class CarModel(models.Model):
model = models.CharField(max_length=50)
available_colors = models.ManytoManyField(Color, through='AvailableColor')
def __unicode__(self):
return self.model
class AvailableColor(models.Model):
car_model = models.ForeignKey(CarModel)
color = models.ForeignKey(Color)
def __unicode__(self):
return unicode(self.color)
class CarData(models.Model):
car_barcode = models.CharField(max_length=50)
car_model = models.ForeignKey(CarModel)
car_color = ChainedForeignKey(
AvailableColor,
chained_field="car_model", # field in CarData
chained_model_field="car_model", # field in AvailableColor
show_all=False,
auto_choose=True
)
You can't do otherwise because you need two models with a matching FK. Using Cars.car_colors.through (that is a valid model on which to query etc) you don't have a good display. With a (dummy) explicit intermediate model you define unicode and the admin should show the correct data.

How to show database in django-admin using filters by default?

I need to filter database by default every time that I see it (when I save changes or when I open database first time).
Can anybody tell me how to do it?
This is possible with custom custom Managers:
Say you have a class called Book:
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
And you want the admin pages for book objects to only show books by Roald Dahl, then you can add a custom manager:
class DahlBookManager(models.Manager):
def get_query_set(self):
return super(DahlBookManager, self).get_query_set().filter(author='Roald Dahl')
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager()
dahl_objects = DahlBookManager()
Then you just need to specify that your ModelAdmin should use the dahl_objects manager, which is explained here.
Here is my models.py:
from django.db import models
class DahlBookManager(models.Manager):
def get_query_set(self):
return super(DahlBookManager, self).get_query_set().filter(processed=False)
class Book(models.Model):
book_name = models.CharField('book',max_length=1000,null=True, blank=True)
url = models.CharField(max_length=1000,null=True, blank=True)
processed = models.BooleanField('Done',)
def __str__(self):
return u'%s' % (self.book_name)
def url1(self):
return '%s' % (self._url, self.url)
site_url1.allow_tags = True
class Admin:
pass
class Meta:
db_table = 'books'
objects = models.Manager()
dahl_objects = DahlBookManager()
here is my admin.py:
from django.contrib import admin
from mybooks.booksdb.models import Book
from django import forms
admin.autodiscover()
class BookAdmin(admin.ModelAdmin):
def queryset(self,request):
qs=self.model.objects.get_query_set()
ordering = self.ordering or ()
if ordering:
qs=qs.order_by(*ordering)
return qs
....
No filter by default. Where is my miss?