Django admin link at user model from another in list - python-3.x

In my django admin i have a model that link to user, i would have n my list create a link for pass to related user id editing page directly from connected models, i do:
class a_cards(models.Model):
CK_CHOICES = (
('A', 'Ambassador'),
('M', 'Medico'),
('P', 'Paziente'),
('S', 'Senator'),
)
c_num = models.CharField(max_length=200, unique=True,
null=True, blank=True, verbose_name="Numero card")
c_data = models.DateTimeField(auto_now=True)
c_type = models.CharField(
max_length=1, choices=CK_CHOICES, verbose_name="Tipo utenza")
c_email = models.CharField(max_length=200, unique=True,
null=True, blank=True, verbose_name="Notifica Email")
c_agente = models.ForeignKey(AgenteProfile, on_delete=models.CASCADE,
null=True, blank=True, related_name="ag_id", verbose_name="Agente")
c_user = models.OneToOneField(User, null=True, blank=True, on_delete=models.CASCADE, verbose_name="Cliente" )
class Meta:
verbose_name = 'Cards'
verbose_name_plural = 'Cards'
def user_link(self):
if self.c_user:
return '%s' % (reverse("admin:auth_user_change", args=(self.c_user.id,)) , escape(self.c_user))
user_link.allow_tags = True
user_link.short_description = "User"
def __str__(self):
return self.c_num
in admin:
class cardAdmin(ImportExportModelAdmin):
list_filter = ('c_type',)
list_display = ('c_num', 'c_data', 'c_type', 'c_agente', 'user_link',)
...
but when i open my admin page i see the correct link but not in libk form:
where is the error?
why i can't have clickable link?
So many thanks in advance

you should use format_html for building HTML contents. so refactor your url generator method like;
class a_cards(models.Model):
....
def user_link(self):
if self.c_user:
return format_html('{text}', url=reverse("admin:auth_user_change", args=(self.c_user.id,)), text=self.c_user)
return "-"
user_link.short_description = "User"
user_link.allow_tags = True
from the docs;
All args and kwargs are passed through conditional_escape() before being passed to str.format().
so you do not need to specify extra escape().

Related

store phone field use django-phonenumber-field override search method to e164 format

i have store phone field to database using django-phonenumber-field with e164 format, its success formated and succesfully.
this is my model.py
class External(models.Model):
name = models.CharField(max_length=255, unique=True)
address = models.CharField(max_length=255)
type= models.ForeignKey(Type, on_delete=models.CASCADE, verbose_name="Type")
region= models.ForeignKey(Region, on_delete=models.CASCADE, verbose_name="Region")
email = models.EmailField(max_length=254, unique=True)
phone = PhoneNumberField(null=True, blank=True, unique=True)
status = models.BooleanField(default=True)
created_at = models.DateTimeField('date created', auto_now_add=True)
updated_at = models.DateTimeField('last edited', auto_now=True)
def __str__(self):
return self.name
but the problem is when i seacrh using national format like 0216333007 no data found.
can someone help me to override the search methode when user input 0216333007 auto format to e164 = +62216333007 ?
this is my admin.py
exclude = ()
form = ExternalAdminForm
list_per_page = 10
readonly_fields = ('created_at', 'updated_at')
list_display = ('name', 'type_id_slug', 'short_address', 'region_id_slug', 'email_link', 'phone', 'status', 'created_at', 'updated_at')
list_filter = (StatusFilter, TypeFilter, RegionFilter,)
search_fields = ('name', 'address', 'email', 'phone',)
im using django 4.1.5
thanks to anyone who helps me.
sorry for my bad english.
i found the answer
from phonenumber_field.modelfields import PhoneNumberField
from phonenumber_field.phonenumber import PhoneNumber
search_fields = ('name', 'address', 'email', 'phone',)
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
try:
search_term = PhoneNumber.from_string(search_term, None).as_e164
queryset |= self.model.objects.filter(phone=search_term)
except:
pass
return queryset, use_distinct

EcomUser() got an unexpected keyword argument 'mobile_number'

I'm trying to create a user and link it with the user profile. But here is the problem.
This is my profile model
class EcomUser(BaseModelMixin):
profile = models.OneToOneField(
settings.AUTH_USER_MODEL, unique=True, blank=True, null=True,
on_delete=models.CASCADE)
username = models.CharField(max_length=100, blank=True)
email = models.CharField(max_length=100, blank=True, null=True)
name = models.CharField(max_length=100, blank=True)
referral_code = models.CharField(max_length=10, blank=True)
signinId = models.CharField(max_length=20, blank=True)
GOOGLE = 'GOOGLE'
APPLE = 'APPLE'
MOBILE_NUMBER = 'mobile_number'
LOGIN_TYPES = (
(GOOGLE, 'GOOGLE'),
(APPLE, 'APPLE'),
(MOBILE_NUMBER, 'mobile_number')
)
loginType = models.CharField(max_length=15, choices=LOGIN_TYPES, default=MOBILE_NUMBER)
class Meta:
db_table = "ecom_user"
My user model:
class User(AbstractBaseUser, BaseModelMixin):
email = models.EmailField(
verbose_name='email_address',
max_length=255,
blank=True,
null=True
)
username = models.CharField(max_length=128, null=True,unique=True)
fullName = models.CharField(max_length=256, blank=True)
mobile_number = models.CharField(max_length=15, blank=True,unique=True)
is_superuser = models.BooleanField(default=False)
class Meta:
db_table = "users"
USERNAME_FIELD = 'username'
REQUIRED_FIELDS = [] # Email & Password are required by default.
def get_full_name(self):
# The user is identified by their email address
return self.fullName
def __str__(self): # __unicode__ on Python 2
return self.email
def has_perm(self, perm, obj=None):
"Does the user have a specific permission?"
# Simplest possible answer: Yes, always
return True
def has_module_perms(self, app_label):
"Does the user have permissions to view the app `app_label`?"
# Simplest possible answer: Yes, always
return True
objects = UserManager()
and serializer.py
class UserRegisterSerializer(serializers.Serializer):
"""Normal serializer which accepts mobile number"""
mobile_number = serializers.CharField(max_length=10, min_length=10, required=True)
referral_code = serializers.CharField(max_length=10, min_length=10, required=True)
email = serializers.EmailField(max_length=255)
def validate_referral_code(self, referral_code):
if not re.match(r"^[\w]{10}$", referral_code):
raise serializers.ValidationError("Wrong Referral Code")
return referral_code
def validate_mobile_number(self, mobile_number):
if not re.match(r"^[\d]{10}$", mobile_number):
raise serializers.ValidationError("Please pass proper mobile number")
return mobile_number
class EcomUserSerializer(serializers.ModelSerializer):
profile_id = serializers.IntegerField(required=True)
mobile_number = serializers.CharField(max_length=10, min_length=10, required=True)
referral_code = serializers.CharField(max_length=10, min_length=10, required=True)
class Meta:
model = EcomUser
fields= ('profile_id','referral_code', 'mobile_number', 'loginType', 'signinId')
def create(self, data):
instance = EcomUser.objects.create(**data)
return instance
views.py
#api_view(['POST'])
def registration(request):
try:
local_reponse = {
"status": False,
"message": "",
"code": status.HTTP_400_BAD_REQUEST,
"data": []
}
data = request.data
print('1')
mobile_number = data.get('mobile_number', None)
referralCode = data.get('referralCode', '')
LoginType = data.get('LoginType')
signinId = data.get('signinId', '')
email = data.get('email', '')
print(f'{LoginType} --- {EcomUser.MOBILE_NUMBER}')
if LoginType == "mobile_number":
print('2')
exist_user = User.objects.filter(mobile_number=mobile_number).count()
if exist_user:
print('3')
local_reponse["message"] = msg.ALREADY_EXIST
return Response(local_reponse)
try:
print('not found')
registerd_user = User.objects.get(mobile_number=mobile_number)
print(f'{registerd_user} -- check')
if registerd_user:
print('register')
ecom = EcomUser.objects.get(profile_id=registerd_user.id)
if ecom:
local_reponse["message"] = msg.ALREADY_EXIST
except User.DoesNotExist:
print('4')
user = User.objects.create_ecom_user(mobile_number=mobile_number)
user.save()
print('is user create?')
if user:
print('yes')
info = {}
info['profile_id'] = user.id
info['referral_code'] = referralCode
info['mobile_number'] = mobile_number
info['LoginType'] = LoginType
info['signinId'] = signinId
ecom_serializer = EcomUserSerializer(data=info)
if not ecom_serializer.is_valid():
print('5')
local_reponse["message"] = ecom_serializer.error_messages
local_reponse["status"] = False,
return Response(local_reponse)
print(f'linked profile 1 ----' )
ecomuser_data = ecom_serializer.save()
print(f'linked profile 2 ---- ' )
ecomuser_id = ecomuser_data.id
print('linked profile 3' )
ecomuser = EcomUser.objects.get(id=ecomuser_id)
print('linked profile 4' )
ecom_serializer = EcomUserSerializer(ecomuser)
print('linked profile 5' )
context = ecom_serializer.data
print('linked profile 6' )
local_reponse = {
"status": True,
"message": msg.CREATED,
"code": status.HTTP_201_CREATED,
"data": context
}
print('linked profile 7' )
return Response(local_reponse)
except Exception as e:
print('7')
local_reponse["message"] = str(e)
return Response(local_reponse)
When I try to run API on postman. It showed "EcomUser() got an unexpected keyword argument 'mobile_number'" and the output in the terminal show below:
1
mobile_number --- mobile_number
2
not found
4
is user create?
yes
linked profile 1 ----
7
The user is created but can not link to the profile, any one can help?
There's no mobile_number field in your EcomUser model. Thus when you call
ecomuser_data = ecom_serializer.save()
after adding
info['mobile_number'] = mobile_number
if fails, leaving you with the user and no profile. I guess is_valid() is only checking the fields it wants, not detecting the surplus, but the extra field causes the save to fail with the error shown. You've already added the field when creating the user, so you don't need it in the profile, so removing that second line should make things work.

django UpdateView get_success_url not returning to profile page

I am still new to django and have encountered this issue, the situation is like this, I have a profile model, on which I have 2 views ViewProfile and EditProfile inheriting from DetailView and UpdateView respectively. when I edit the profile page, it doesn't get me to the profile page instead it gave the error:
Reverse for 'profile' with keyword arguments '{'id': 9}' not found. 1 pattern(s) tried: ['profile/(?P<pk>[^/]+)/\\Z']
even though I have checked in the python shell, the profile with id:9 is indeed profile with name muham see below
>>> Profile.objects.all()
<QuerySet [<Profile: huzaifa>, <Profile: another1>, <Profile: muham>]>
>>> p1 = Profile.objects.get(name='muham')
>>> p1.id
9
>>>
but still its not showing,
I have overridden the get_success_url to get me to the profile page:
class EditProfile(LoginRequiredMixin, UpdateView):
model = Profile
fields = ('name', 'address', 'phone_no',)
template_name = 'blog_app/edit_profile.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['user'] = self.request.user
return context
def get_success_url(self):
id = self.request.user.profile.id
return reverse_lazy('profile', kwargs={'id': id})
my model is below:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)
name = models.CharField(max_length=60, blank=True, null=True)
address = models.CharField(max_length=400, null=True, blank=True)
# image
phone_no = models.CharField(max_length=40, null=True, blank=True)
def __str__(self):
return str(self.user)
class ViewProfile(LoginRequiredMixin, DetailView):
model = Profile
template_name = 'blog_app/profile.html'
context_object_name = 'profile'
urls.py:
urlpatterns = [
path('', ListBlog.as_view(), name='home'),
path('my-blogs/', MyBlog.as_view(), name='my-blogs'),
path('create-blog/', CreateBlog.as_view(), name='blog-create'),
path('blog-detail/<int:pk>/', BlogDetail.as_view(), name='blog-detail'),
path('edit-blog/<int:pk>/', EditBlog.as_view(), name='blog-edit'),
path('delete-blog/<int:pk>/', DeleteBlog.as_view(), name='blog-delete'),
path('profile/<str:pk>/', ViewProfile.as_view(), name='profile'),
path('edit-profile/<str:pk>/', EditProfile.as_view(), name='edit-profile'),
]
I have found my error, it was because DetailView uses pk or slug, on the urlconf arguments, and before i have added str:id instead of str:pk, and also in the
def get_success_url(self):
id = self.request.user.profile.id
return reverse_lazy('profile', kwargs={'id': id})
i had kwargs={'id':id} instead of {"pk": id}. so that was my issue, and it was resolved after that.

Django rest framework create object with many-to-many filed serializer

I can't create a project object with many-to-many field to languages model. I need to find the best way to implement this. I have tried the django rest framework docs, but it didn't work for me. Do the language objects need to be created before the project object or DRF will handle this? This problem occurs when I added the owner field, then I needed to add the create method inside the ProjectSerializer to save the object owner, but then this error occurred: Direct assignment to the forward side of a many-to-many set is prohibited. Use languages.set() instead.
I tried languages.set() but it still does not work.
models.py
from django.db import models
from django.contrib.auth.models import User
class Language(models.Model):
name = models.CharField(max_length=100, blank=True, default='')
description = models.TextField()
owner = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ['id']
def __str__(self):
return self.name
class Developer(models.Model):
name = models.CharField(max_length=100, blank=True, default='')
age = models.IntegerField()
bio = models.TextField()
created = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ['created']
def __str__(self):
return self.name
class Project(models.Model):
name = models.CharField(max_length=100, blank=True, default='')
stars = models.IntegerField()
developer = models.ForeignKey(Developer, related_name='projects', on_delete=models.CASCADE)
languages = models.ManyToManyField(Language)
owner = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ['stars']
def __str__(self):
return self.name
serializers.py
from rest_framework import serializers
from developers.models import Language, Developer, Project
class ProjectSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
class Meta:
model = Project
fields = ['id', 'name', 'stars', 'developer', 'languages', 'owner', ]
def create(self, validated_data):
project = Project.objects.create(owner=self.context['request'].user, **validated_data)
return project
class LanguageSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
projects = ProjectSerializer(read_only=True, many=True)
class Meta:
model = Language
fields = ['id', 'name', 'description', 'projects', 'owner', ]
def create(self, validated_data):
language = Language.objects.create(owner=self.context['request'].user, **validated_data)
return language
class DeveloperSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
projects = ProjectSerializer(read_only=True, many=True)
class Meta:
model = Developer
fields = ['id', 'name', 'age', 'bio', 'projects', 'owner', ]
def create(self, validated_data):
developer = Developer.objects.create(owner=self.context['request'].user, **validated_data)
return developer

How to custom a search filter in django rest framework

Model
class Student(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
first_name = models.CharField(verbose_name=_('first name'), max_length=20, blank=True, null=True)
last_name =models.CharField(verbose_name=_('last name'), max_length=20, blank=True, null=True)
class Bicycle(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4, editable=False)
name = models.CharField(verbose_name=_('bicycle name'), max_length=20, blank=True, null=True)
student_id = models.UUIDField(default=uuid4)
View:
class BicycleList(AdUpdateMixin, AdDestroyMixin, AdListMixin, AdCreateMixin, generics.GenericAPIView):
permission_classes = [IsAuthenticated]
queryset = Bicycle.objects.all()
search_fields = ['name', 'id', 'student_id']
Now, I want create a custom search field in django rest framework. So that I can search first_name and last_name in Bicycle List ?
If you can use student as a foreign key in Bicyle Model then you can query as:
from django.db.models import Q
Bicycle.objects.filter(
Q(student__first_name__icontains=t)
| Q(student__first_name__icontains=t)
)

Resources