Django restrict foreign key options - django

So I have three models:
class Session(models.Model):
id = models.UUIDField('ID', default=uuid.uuid4, primary_key=True, editable=False)
start_time = models.TimeField('Start Time', default=None)
end_time = models.TimeField('End Time', default=None)
def __str__(self):
return "{}-{}".format(str(self.start_time), str(self.end_time))
class Slot(models.Model):
id = models.UUIDField('ID', default=uuid.uuid4, primary_key=True, editable=False)
timings = models.ForeignKey('Session', on_delete=models.DO_NOTHING, related_name='slot_timings')
available_counsellors = models.ManyToManyField(User, limit_choices_to={'role': 'COUNSELLOR'}, related_name='available_counsellors')
def __str__(self):
return str(self.timings)
class ChatSession(models.Model):
def get_access_code():
while True:
access_code = get_random_string(length=6, allowed_chars=('ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'))
if not ChatSession.objects.filter(access_code=access_code).exists():
return access_code
id = models.UUIDField('ID', default=uuid.uuid4, primary_key=True, editable=False)
client = models.ForeignKey(User, limit_choices_to={'role': 'CLIENT'}, on_delete=models.DO_NOTHING, related_name='client_user')
counsellor = models.ForeignKey(User, limit_choices_to={'role': 'COUNSELLOR'}, on_delete=models.DO_NOTHING, related_name='counsellor_user')
access_code = models.CharField('Access Code', default=get_access_code, max_length=6)
topic = models.CharField('Topic', default=None, blank=True, null=True, max_length=255)
slot = models.ForeignKey(Slot, on_delete=models.DO_NOTHING, default=None)
def __str__(self):
return str(self.topic)
In the chat session model, I want to limit the options for counsellor field to the available_counsellors list in slot model.
How can I do this??
I want the same to reflect in my admin view also.

You need to update the queryset on the counsellor form field.
Assuming you are using a ModelForm, you can do this in your view:
chat_session = ChatSession.objects.get(pk=1)
form = ChatSessionForm(instance=chat_session)
form.fields["counsellor"].queryset = chat_session.slot.available_counsellors
You can alternatively do it in your ModelForm:
class ChatSessionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super (ChatSessionForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.slot:
options = self.instance.slot.available_counsellors
else
options = User.objects.none()
self.fields['counsellor'].queryset = options
For Django admin, something like this should work:
class ChatSessionAdmin(admin.ModelAdmin):
def render_change_form(self, request, context, *args, **kwargs):
if kwargs['obj'].slot:
options = kwargs['obj'].slot.available_counsellors
else:
options = User.objects.none()
context['adminform'].form.fields['counsellor'].queryset = options
return super(ChatSessionAdmin, self).render_change_form(request, context, args, kwargs)

Related

RelatedObjectDoesNotExist at CustomUser has no student

i'm trying to filter logbook report based on the logged in industry supervisor,
the supervisor should be able to see the report of students under his supervision
views.py
class LogbookEntryView(ListAPIView):
queryset = LogbookEntry.objects.all()
serializer_class = StudentLogbookEntrySerializer
def get_queryset(self, *args, **kwargs):
qs = super().get_queryset(*args, **kwargs)
request = self.request
user = request.user
if not user.is_authenticated:
LogbookEntry.objects.none()
return qs.filter(student_id__industrysupervisor = request.user.student.industry_based_supervisor)
models.py
LogbookEntry Model
class LogbookEntry(models.Model):
week = models.ForeignKey("api.WeekDates", verbose_name=_("Week Id"), null=True, on_delete=models.SET_NULL)
student = models.ForeignKey("students.Student", verbose_name=_("Student Id"), on_delete=models.CASCADE)
entry_date = models.DateTimeField()
title = models.CharField(_("Title"), max_length=50)
description = models.CharField(_("Description"), max_length=1000)
diagram = models.ImageField(_("Diagram"), upload_to=profile_picture_dir)
Student Model
class Student(models.Model):
user = models.OneToOneField(get_user_model(), null=True, on_delete=models.CASCADE)
profile_pic = models.ImageField(_("profile picture"), upload_to=profile_picture_dir)
department_id = models.ForeignKey(Department, null=True, on_delete=models.SET_NULL)
phone_no = models.CharField(max_length=11)
school_based_supervisor = models.ForeignKey("school_based_supervisor.SchoolSupervisor", verbose_name=_("School Supervisor"), null=True, on_delete=models.SET_NULL)
industry_based_supervisor = models.ForeignKey("industry_based_supervisor.IndustrySupervisor", verbose_name=_("Industry Supervisor"), null=True, on_delete=models.SET_NULL)
placement_location = models.ForeignKey("industry_based_supervisor.PlacementCentre", verbose_name=_("Placement Location"), null=True, blank=True, on_delete=models.SET_NULL)
Industry Supervisor Model
class IndustrySupervisor(models.Model):
user = models.OneToOneField(get_user_model(), null=True, on_delete=models.CASCADE)
profile_pic = models.ImageField(_("profile picture"), upload_to=profile_picture_dir)
phone_no = models.CharField(max_length=11)
placement_center = models.ForeignKey("industry_based_supervisor.PlacementCentre", verbose_name=_("Placement Centre"), null=True, blank=True, on_delete=models.CASCADE)
def __str__(self):
return self.user.username
You can update your get_queryset method in the LogbookEntryView class
def get_queryset(self):
user = self.request.user
# If user is not authenticated, return empty queryset
if not user.is_authenticated:
return LogbookEntry.objects.none()
# If user is not a student, return empty queryset
if not hasattr(user, 'student'):
return LogbookEntry.objects.none()
# Get the industry supervisor for the student
supervisor = user.student.industry_based_supervisor
# If student does not have an industry supervisor, return empty queryset
if supervisor is None:
return LogbookEntry.objects.none()
# Filter logbook entries based on the industry supervisor
queryset = LogbookEntry.objects.filter(student__industrysupervisor=supervisor)
return queryset

django multiple table query

I have a form for filling out lessons that I want to limit who the students can select as their teacher to only confirmed connections. I have three models:
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=254, null=True, blank=True)
class Lesson(models.Model):
user = models.ForeignKey(User, related_name='fencer', on_delete=models.SET_NULL, null=True, blank=True)
teacher = models.ForeignKey(Fencer, related_name='instructor', on_delete=models.SET_NULL, null=True, blank=True)
lesson_date = models.DateField(default="1900-01-01")
title = models.CharField(max_length=100, null = True, blank=True)
description = models.TextField(null=True, blank=True)
class Connection(models.Model):
student = models.ForeignKey(User, related_name='student', on_delete=models.CASCADE, blank=True)
teacher = models.ForeignKey(User, related_name='teacher', on_delete=models.CASCADE, blank=True)
student_accepts = models.BooleanField(default=False)
teacher_accepts = models.BooleanField(default=False)
#property
def connected(self):
if self.student_accepts == True and self.teacher_accepts == True:
return True
else:
return False
My form so far is:
class LessonForm(ModelForm):
class Meta:
model = models.Lesson
#fields = ()
fields = '__all__'
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['teacher'].queryset = Users.objects.filter() # the best I have so far
How do I filter the User model based on the link made in the Connection model? Maybe I'm overcomplicating this or is there a better way?
Thank you in advance
Found the answer in this other question on here about spanning models.
I had a hard time getting django to see the fields for some reason so I will keep this ugly version.
def __init__(self, user, *args, **kwargs): # will need to pass the user to the form when used
super(LessonForm, self).__init__(*args, **kwargs)
# reduce options to just the coaches that the student is connected with
connected_teachers = Connection.objects.filter(Q(student=user) and (Q(student_accepts=True) and Q(teacher_accepts=True)))
teachers = User.objects.filter(teacher__in=connected_teachers)
self.fields['teacher'].queryset = teachers

Saving data once (to be able to load to images to server) then updating the same instance after some changes

I'm saving the data from the post request a first time because I need the images to be uploaded to the server like the "upload_image" function suggests.
At the same time I have a couple of fields that are null and need to have some values via some external functions.
And then I need to save the whole object in the database.
The problem that I'm facing is that it saves the data twice in the database.
And the first saved object has all attributes null except for passengerPhoto, passengerPassport and agent. ( No errors shown btw)
Any ideas, please?
Thanks a lot!
#VIEW
class IdentityCheckView(CreateAPIView, generics.ListAPIView):
serializer_class = IdentityCheckSerializer
permission_classes = [permissions.AllowAny]
def get_queryset(self):
request = self.request
qs = IdentityCheck.objects.all()
query = self.request.GET.get('q')
if query is not None:
qs = qs.filter(name__icontains=query)
return qs
def post(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
if(serializer.is_valid()):
res = self.create(request, *args, **kwargs)
image = FaceRecognition.imageMatch(res.data['passengerPhoto'], res.data['passengerPassport'])
wanted = WantedPro.criminalMatch(res.data['passengerPhoto'])
passport_json = OCR.passportMatch(res.data['passengerPassport'])
image_json = json.loads(image)
firstName = passport_json['names']
lastName = passport_json['surname']
nationality = passport_json['country']
birthDate = passport_json['date_of_birth']
gender = passport_json['sex']
ableToBoard = bool(wanted) & bool(image_json['match']) & bool(passport_json['valid_expiration_date'])
serializer.update(
id=res.data['id'],
firstName=firstName,
lastName=lastName,
nationality=nationality,
birthDate=birthDate,
gender=gender,
ableToBoard=ableToBoard)
return Response({"image": image_json, "passport": passport_json, "wanted": wanted}, status=200)
def perform_create(self, serializer):
res = serializer.save(agent=self.request.user)
#SERIALIZER
class IdentityCheckSerializer(serializers.ModelSerializer):
class Meta:
model = IdentityCheck
fields = '__all__'
read_only_fields = ['agent', 'id']
#MODEL
def upload_image(instance, filename):
return "media/check/{agent}/{date}/{filename}".format(agent=instance.agent,date=datetime.datetime.today().strftime('%d-%m-%Y'), filename=filename)
class IdentityCheckQuerySet(models.QuerySet):
pass
class IdentityCheckManager(models.Manager):
def get_queryset(self):
return IdentityCheckQuerySet(self.model,using=self._db)
class IdentityCheck(models.Model):
agent = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
passengerPhoto = models.ImageField(upload_to=upload_image, null=False, blank=False)
passengerPassport = models.ImageField(upload_to=upload_image, null=False, blank=False)
lastName = models.CharField(max_length=255, null=True, blank=True)
firstName = models.CharField(max_length=255, null=True, blank=True)
birthDate = models.DateField(null=True, blank=True)
nationality = models.CharField(max_length=255, null=True, blank=True)
gender = models.CharField(max_length=1, null=True, blank=True)
ableToBoard = models.BooleanField(null=False, blank=False, default=False)
timestamp = models.DateTimeField(auto_now_add=True)
objects = IdentityCheckManager()

Django models Foreign key autopopulate

Am having two model classes user and accounts linked by together or connected by another table called useraccounts.
What i want is for the useraccounts table to be automatically also be populated with the ids of the user and account table when i submit data to them(user and accounts).
here is my sample models code.
class Account(models.Model):
# fields
id = models.IntegerField(primary_key=True)
t_stamp = models.DateField(default=datetime.datetime.now())
acctno = models.TextField(null=False, unique =True)
acctname = models.TextField(null=False)
status = models.TextField(null=False, choices=STATUS)
accttype = models.TextField(null=False,choices=ACCT_TYPE)
acctclass = models.TextField(null=False, choices=ACCT_CLASS)
min_balance = models.FloatField(default=0)
cur_balance = models.FloatField(default=0)
ava_balance = models.FloatField(default=0)
#fundingsources = models.ManyToManyField(FundingSource)
class Meta:
managed = False
db_table = 'accounts'
def save(self, *args, **kwargs):
super(Account, self).save(*args, **kwargs)
try:
self.useraccount_set.all()[0]
except:
UserAccount.objects.bulk_create([UserAccount(account_id=self,user_id=user) for user in User.objects.all()])
class User(AbstractBaseUser, PermissionsMixin):
id = models.AutoField(primary_key=True)
username = models.TextField(unique=True, null=True)
fullname = models.TextField(null=False)
country = models.TextField(null=True)
email = models.EmailField( unique=True, db_index=True)
phone = models.TextField()
address = models.TextField()
activation_key = models.CharField(max_length=40)
key_expires = models.DateTimeField(null=True)
date_joined = models.DateTimeField(default=timezone.now)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=True)
#accounts = models.ManyToManyField(Account, through='UserAccount')
class Meta:
db_table = "users"
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserManager()
def get_short_name(self):
return self.fullname
def get_username(self):
return self.email
def is_authenticated(self):
return True
#property
def is_staff(self):
"Is the user a member of staff?"
# Simplest possible answer: All admins are staff
return self.is_admin
signals.post_save.connect(create_auth_client, sender=User)
User._meta.get_field_by_name('email')[0]._unique=True
class UserAccount(models.Model):
user_id = models.ForeignKey(User)
account_id = models.ForeignKey(Account)
class Meta:
managed = False
db_table = 'useraccounts'
class UserAccount(models.Model):
user_id = models.ForeignKey(User)
account_id = models.ForeignKey(Account)
class Meta:
managed = False
db_table = 'useraccounts'
You have to do that in model save method:
class Account(models.Model):
id = models.IntegerField(primary_key=True)
def save(self, *args, **kwargs):
super(Account, self).save(*args, **kwargs)
try:
self.useraccount_set.all()[0]
except:
UserAccount.objects.bulk_create([UserAccount(account_id=self,user_id=user) for user in User.objects.all()])
class User(AbstractBaseUser, PermissionsMixin):
id = models.AutoField(primary_key=True)
def save(self, *args, **kwargs):
super(User, self).save(*args, **kwargs)
try:
self.useraccount_set.all()[0]
except:
UserAccount.objects.bulk_create([UserAccount(user_id=self,account_id=account) for account in Account.objects.all()])

How to create an inline formset for a reverse foreign key relationship

I have a Property Model as follows =
class Property(models.Model):
property_type = models.CharField(max_length=255, default='Apartment')
specifications = models.CharField(max_length=255, default='Basic')
built_up_area = models.FloatField(max_length=6, null=False, default=0)
total_area = models.FloatField(null=False, default=0)
number_of_bedrooms = models.CharField(max_length=3, default=1)
number_of_bathrooms = models.CharField(max_length=3, default=1)
number_of_parking_spaces = models.CharField(max_length=2, default=0)
address_line_one = models.CharField(max_length=255, null=False)
address_line_two = models.CharField(max_length=255, null=True, default=None)
connectivity = models.CharField(max_length=255, default=None, null=True)
neighborhood_quality = models.CharField(max_length=255, default=None,
null=True)
comments = models.CharField(max_length=255, default=None, null=True)
city = models.ForeignKey('City')
state = models.ForeignKey('State')
pin_code = models.ForeignKey('PinCode')
developer = models.ForeignKey('Developer', null=True, default=None)
owner = models.ForeignKey('Owner', null=True, default=None)
created_by = models.ForeignKey('custom_user.User')
project = models.ForeignKey('Project')
def __unicode__(self):
return self.property_type
class Meta:
verbose_name_plural = 'Properties'
And a City model as follows -
class City(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(City, self).save(*args, **kwargs)
def __unicode__(self):
return self.name
Now I want to make a single form where I can enter the Property details and while entering the city, I can enter the name of the city instead of selecting from the dropdown list.
So how do I create the inline formset using the inlineformset_factory to create the form?
==EDIT==
I've tried to use the following code to create the formset
CityFormset = inlineformset_factory(City, Property,
fields=('city',),
extra=0,
min_num=1,
can_delete=False)
You've misunderstood what an inline formset is. It's for editing the "many" side of a one-to-many relationship: that is, given a parent model of City, you could edit inline the various Properties that belong to that city.
You don't want a formset at all to simply edit the single City that a property can belong to. Instead, override the city field within your Property form to be a TextField, and either create a new City or find an existing one in the clean_city method.
class PropertyForm(forms.ModelForm):
city = forms.TextField(required=True)
class Meta:
model = Property
exclude = ('city',)
def __init__(self, *args, **kwargs):
super(PropertyForm, self).__init__(*args, **kwargs)
if self.instance and not self.data:
self.initial['city'] = self.instance.city.name
def save(self, commit=True):
city_name = self.cleaned_data['city']
city, _ = City.objects.get_or_create(name=city_name)
instance = self.save(commit=False)
instance.city = city
if commit = True:
instance.save()
return instance