How to render choices in a Django field based on a condition - python

I have a custom user model:
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
is_worker = models.BooleanField(default=False)
is_customer = models.BooleanField(default=True)
def __str__(self):
return f'{self.username} - {self.is_worker}'
And I have this model services :
from django.db import models
from users.models import CustomUser
SERVICE_CHOICES = (
('Carpenter', 'Carpenter'),
('Driver', 'Driver'),
('Ambulanve', 'Ambulanve'),
('Spa', 'Spa'),
('Barber', 'Barber'),
('Cleaning', 'Cleaning'),
('Cook', 'Cook'),
)
class Service(models.Model):
name = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
service = models.CharField(choices=SERVICE_CHOICES,max_length=100)
def __str__(self):
return f'{self.service} - {self.name}'
In services model, I am using CustomUser model for the name. Whereas, I need only those names where is_worker = True. In my code, I am getting all the users irrespective of the status. How can I change it?

You can use ForeignKey.limit_choices_to to set a limit of available choices for a foreignkey field. In your case :
class Service(models.Model):
name = models.ForeignKey(CustomUser, on_delete=models.CASCADE, limit_choices_to={'is_worker': True},)
service = models.CharField(choices=SERVICE_CHOICES,max_length=100)
def __str__(self):
return f'{self.service} - {self.name}'
Django doc

something like that:
class ServiceForm(ModelForm):
def __init__(
self,
*args,
**kwargs
):
super().__init__(*args, **kwargs)
if self.instance and self.instance.is_worker:
self.fields['service'].widget = forms.Select(
choices=[(k, v)
for i in SERVICE_CHOICES if v in ('Barber', 'Spa')]
)

Related

Django Unit Test Case for Models, Serializers, URL's & Views

I'm a newbie to Unit testing. As I need to perform test case for following models, serializers, views & urls. Can anyone please help.
Models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.contrib.auth import get_user_model
# Create your models here.
class User(AbstractUser):
"""This Class is used to extend the in-build user model """
ROLE_CHOICES = (('CREATOR','CREATOR'),('MODERATOR','MODERATOR'),('USERS','USERS'))
GENDER_CHOICES = (('MALE','MALE'),('FEMALE',"FEMALE"),('OTHER','OTHER'))
date_of_birth = models.DateField(verbose_name='Date of Birth', null=True)
profile_image = models.ImageField(upload_to='media/profile_images', verbose_name='Profile Image', default='media/profile_images/default.webp', blank=True)
bio = models.TextField(verbose_name='Bio')
role = models.CharField(max_length=10, verbose_name='Role', choices=ROLE_CHOICES)
gender = models.CharField(max_length=6, verbose_name='Gender', choices=GENDER_CHOICES)
Serializers.py
class UserSerializer(serializers.ModelSerializer):
following = serializers.SerializerMethodField()
followers = serializers.SerializerMethodField()
class Meta:
model = User
fields = ('first_name','last_name','username','password','email','date_of_birth',
'profile_image','bio','role','gender', 'following','followers')
extra_kwargs = {'is_active':{'write_only':True},
'password':{'write_only':True}}
def create(self, validated_data):
logger.info('Information Incoming!')
return User.objects.create_user(**validated_data)
def update(self, *args, **kwargs):
user = super().update( *args, **kwargs)
p = user.password
user.set_password(p)
user.save()
return user
def get_following(self, obj):
return FollowingSerializer(obj.following.all(), many=True).data
def get_followers(self, obj):
return FollowersSerializer(obj.followers.all(), many=True).data
Views.py
class UserAPI(viewsets.ModelViewSet):
serializer_class = UserSerializer
queryset = User.objects.all()
# permission_classes = [permissions.IsAuthenticated, TokenHasReadWriteScope]
I tried implementing unit test case but getting an error self.assertEquals(user.get_last_name(), "Johnson") - AttributeError: 'User' object has no attribute 'get_last_name'
tests.py
class UserTests(TestCase):
def setUp(self):
User.objects.create(
first_name='Louis',last_name='Johnson',username='louis.johnson',
email='louis#mail.com',date_of_birth='1994-12-12', bio='Hello I am Louis',
role='MODERATOR',gender='MALE')
def test_users_model(self):
user = User.objects.get(first_name='Louis')
self.assertEquals(user.get_last_name(), "Johnson")
self.assertEquals(user.get_username(), "louis.johnson")
Can anyone please help in sorting out these. Any detailed documentation or help from your end would be a great support.
The user object does not have a function like get_last_name. You can call the attribute directly like that:
def test_users_model(self):
user = User.objects.get(first_name='Louis')
self.assertEquals(user.last_name, "Johnson")
...

Django , how to show 'secondary property' of parent in TabularInline

I try to do tabular inline admin.
In the child tab, if we include a ForeignKey field, it will show the str property of that foreign model.
But how to also show another property of that foreign model?
Here is my models.py
class RawMaterial(models.Model):
name = models.CharField(max_length=15)
ubuy = models.CharField(max_length=5)
usell = models.CharField(max_length=5)
uconv = models.DecimalField(max_digits = 5,decimal_places=2)
def __str__(self):
return self.name
class Coctail(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Ingredient(models.Model):
coctail = models.ForeignKey(Coctail,
related_name='ingredient',
on_delete=models.CASCADE)
rawmaterial = models.ForeignKey(RawMaterial,
related_name='ingredient',
on_delete=models.CASCADE)
qty = models.DecimalField(max_digits = 5,decimal_places=2)
def __str__(self):
return self.rawmaterial
def rawusell(self):
return self.rawmaterial.usell
rawusell.short_description = 'UOM'
Here is my admin.py
from django.contrib import admin
# Register your models here.
from .models import *
admin.site.register(RawMaterial)
class IngredientInline(admin.TabularInline):
model = Ingredient
list_display = ('rawmaterial', 'qty', 'rawusell')
class CoctailAdmin(admin.ModelAdmin):
inlines = [IngredientInline]
admin.site.register(Coctail, CoctailAdmin)
and here what I got
My question is : How to show rawmaterial.usell in Ingredient tab?
Sincerely
-bino-
You can show the rawmaterial.usell field in the ingredient tab but it will not be editable. Since, any field can only be editable if they are a field of that model (Without using any custom form and logic).
So, if you want rawmaterial.usell to be editable, you will have to make a rawmaterial admin
You can show the rawmaterial.usell in IngredientInline by doing this.
class IngredientInline(admin.TabularInline):
model = Ingredient
readonly_fields = ('rawusell', )
list_display = ('rawmaterial', 'qty', 'rawusell')
def rawusell(self, obj):
return obj.rawmaterial.usell
This will start showing usell in the inline admin.

UpdateView does not show existing data when using filter

I am quite new with Django and I need help.
My problem is quite similar what Mike had in his case:
UpdateView not populating form with existing data, but I have not found solution yet.
My goal is to view owner dropdown selection list only those users who are members of the organization.
models.py
# organizations.models.py
...
from accounts.models import User
from core.models import TimeStampModel
...
class Organization(TimeStampModel, models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(
verbose_name=_('Name'),
max_length=255,
unique=True
)
code = models.CharField(
verbose_name=_('Code'),
max_length=255,
null=True,
blank=True
)
owner = models.ForeignKey(
User,
on_delete=models.PROTECT,
verbose_name=_('Owner'),
related_name='owner',
help_text=_('Organization Owner and Contact Person'),
)
slug = models.SlugField(verbose_name=_('Organization key'), unique=True)
...
class Meta:
verbose_name = _('Organization')
verbose_name_plural = _('Organization')
ordering = ['name', 'code']
def __str__(self):
return f'{self.name}, {self.code}'
# Create automatically slug value from organization name field.
# In case similar is exist then add extra count digit end of slug.
def _get_unique_slug(self):
slug = slugify(self.name)
unique_slug = slug
num = 1
while Organization.objects.filter(slug=unique_slug).exists():
unique_slug = '{}-{}'.format(slug, num)
num += 1
return unique_slug
def save(self, *args, **kwargs):
if not self.slug:
self.slug = self._get_unique_slug()
self.next_update = timezone.now() + relativedelta(
months=self.update_interval)
super(Organization, self).save(*args, **kwargs)
def get_absolute_url(self):
kwargs = {
'slug': self.slug
}
return reverse('organization_main_page', kwargs=kwargs)
class OrganizationMembers(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
organization = models.ForeignKey(
Organization,
on_delete=models.CASCADE,
verbose_name=_('Organization')
)
member = models.ForeignKey(
User,
on_delete=models.CASCADE,
verbose_name=_('Member'),
null=True,
blank=True
)
organization_admin = models.BooleanField(
verbose_name=_('Organization admin'),
default=False
)
class Meta:
verbose_name = _('Organization: Member')
verbose_name_plural = _('Organization: Members')
ordering = ['organization', 'member']
unique_together = ('organization', 'member')
def __str__(self):
return f'{self.member}'
def get_absolute_url(self):
kwargs = {
'slug': self.slug
}
return reverse('organization_detail', kwargs=kwargs)
forms.py
# organizations.forms.py
....
from accounts.models import User
from .models import Organization, OrganizationMembers
...
class OrganizationUpdateForm(forms.ModelForm):
class Meta:
model = Organization
fields = '__all__'
exclude = ('date_created', 'created_by', 'created_by_id',
'last_updated', 'last_updated_by', 'last_updated_by_id',
'next_update', 'slug')
# Restrict user selection lists to view only members of the organization
def __init__(self, *args, **kwargs):
inst = kwargs.get('instance', None)
super(OrganizationUpdateForm, self).__init__(*args, **kwargs)
self.fields['owner'].queryset = OrganizationMembers.objects.\ # <--- !!!
filter(organization_id=inst.id)
In the forms.py, if I comment out self.field['owner]... line, then owner field will show saved value from database, but then I can see all users in the dropdown list. When queryset is enabled then selection list show correct users, but saved value is not visible.
views.py
# organizations.views.py
from .forms import OrganizationUpdateForm
from accounts.models import User
from .models import Organization, OrganizationMembers
class OrganizationUpdateView(LoginRequiredMixin, UpdateView):
model = Organization
form_class = OrganizationUpdateForm
template_name = 'organizations/organization_update.html'
success_url = reverse_lazy('organizations')
# Save data and set current user to last updated by fields
def form_valid(self, form):
object = form.save(commit=False)
object.last_updated_by = self.request.user.get_full_name()
object.last_updated_by_id = self.request.user
return super(OrganizationUpdateView, self).form_valid(form)
def get_queryset(self):
criteria1 = Q(owner=self.request.user)
criteria2 = Q(organizationmembers__member=self.request.user)
criteria3 = Q(organizationmembers__organization_admin=1)
org_list = Organization.objects.\
filter(criteria1 | (criteria2 & criteria3)).distinct()
if org_list.count() != 0:
return org_list
else:
raise Http404('You don\'t have permissions!')
In Mikes case Chiheb has commented that "With UpdateView it's a little bit tricky. So, in order to initialize your form's data, you need to do it in the view itself not in the form."
What is the reason that cannot add filter to UpdateView?
Please can someone help me to solve my problem. Thanks.
UPDATE
Not filtered. Value from database is visible
Not filtered. Dropdown list show all users in the system
Filter enabled. Value is not visible
Filter enabled. Dropdown list show correct valeus
The problem is that owner in your models is a FK to User model, but you are filtering queryset in form by OrganizationMembers. Make it the same and the problem should be gone.

Django-filters how to pass current user from view inside FilterSet?

I have a filter where I need to access the request.user. However, Django-filter does not pass it. I was able to figure out possible FilterSet configuration.
But how to pass current user from view inside FilterSet?
filters.py
class TransationsListFilter(django_filters.FilterSet):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(TransationsListFilter, self).__init__(*args, **kwargs)
transaction_date = DateFromToRangeFilter(widget=RangeWidget(attrs {'placeholder': 'DD/MM/YYYY'}))
class Meta:
model = Transations
fields = ['transaction_date']
#property
def qs(self):
return super(TransationsListFilter, self).filter(user=user)
views.py
class TransactionsList(PagedFilteredTableView):
model = Transations
table_class = TransactionsTable
filter_class = TransationsListFilter
formhelper_class = TransationsFormHelper
models.py
class Transations(models.Model):
transaction_date = models.DateField(default=datetime.now, blank=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
default = 0
)
I solved my problem with django-crum plugin.
Here is my final code(other files are the same):
filters.py
import django_filters
from django_filters import DateFromToRangeFilter
from django_filters.widgets import RangeWidget
from .models import Transations,Account
from crum import get_current_user
class TransationsListFilter(django_filters.FilterSet):
transaction_date = DateFromToRangeFilter(widget=RangeWidget(attrs={'placeholder': 'DD/MM/YYYY'}))
class Meta:
model = Transations
fields = ['transaction_date']
#property
def qs(self):
parent = super(TransationsListFilter, self).qs
return parent.filter(user=get_current_user())

Django auth group model m2m

Recently, I have been working in a generic inscription system to help students to register in a class or a lab. But I have problems with the logic of ManyToManyField in the Lab class.
from django.db import models
from django.contrib.auth.models import User
class Day(models.Model):
name = models.CharField(max_length=20, primary_key=True)
def __unicode__(self):
return u"%s" % self.name
class LabName(models.Model):
lab_name = models.CharField(max_length=50, primary_key=True)
class Meta:
verbose_name_plural = 'LabNames'
def __unicode__(self):
return u'%s' % self.lab_name
class Lab(models.Model):
name = models.ForeignKey(LabName)
start_hour = models.TimeField()
length = models.IntegerField(help_text="Given in minutes")
classDays = models.ManyToManyField(Day)
start_date = models.DateField()
finish_date = models.DateField()
max_cap = models.SmallIntegerField(help_text="Maximun number of students")
teacher = models.ForeignKey(User, related_name="Teachers")
students = models.ManyToManyField(User)
class Meta:
verbose_name_plural = 'Labs'
def __unicode__(self):
return u"%s %s" % (self.id, self.name)
I would prefer to associate an specific Group (django.contrib.auth.models.Group) called 'Students' rather than all the users or at least filter and/or validate this field to just add and view students and also do the same with the teacher field.
Update 1: I just noticed that maybe I could filter those users who are in a certain group using the optional parameter limit_choices_to.
The question is:
How can I use the limit_choices_to parameter to show only those users who are in the 'Students' group or the 'Teachers' group?
Ah, see that's MUCH clearer.
I would re-write {'id__in' : Group.objects.all().get(name='Teachers').user_set.all()}
to:
{'groups__name' : 'Teachers' }
Also, if you would like to keep your admin functionality separated from your models more (did you know Admin was originally completely in the models?), you can use formfield_for_foreignkey which is certainly a nice way to keep your models separated from admin junk.
class MyModelAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "teacher":
kwargs["queryset"] = User.objects.filter(groups__name='Teacher')
if db_field.name == "students":
kwargs["queryset"] = User.objects.filter(groups__name='Student')
return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

Categories

Resources