Django admin - Foreign key to display its values (not inlines) - python

earlier this week, I asked this question about having foreign keys in your main model from your subclasses: Django Form With Foreign Key
I used the answer given to make a model and sub-models (code at the end). My question is, I know about admin inlines for foreign keys, but I can't use that since the main model has the foreign key to the subclasses, not the other way around. I want the foreign keys in my main class to be displayed in the admin.
Sorry it that sounds confusing, here's my model:
Subclass 1 PreObservation
class PreObservation( models.Model ):
pre_observation = models.CharField(
max_length=255,
choices=OBS_STANDARD_TYPES,
verbose_name="Pre-Observation Standard"
)
obs__meter_reading = models.FloatField( blank=True, null=True )
obs_if_other = models.FloatField( blank=True, null=True )
Subclass 2 FieldObservation
class FieldObservation( models.Model ):
site_id = models.CharField( max_length=255, choices=STATION_CHOICES )
site_name = models.CharField( max_length=255 )
stage_reading = models.FloatField( )
specific_conductance = models.FloatField( )
water_temp = models.FloatField( )
Main class Record
class Record( models.Model ):
observers = models.CharField( max_length=255, verbose_name="Name of Observer(s)")
pre_observation_standard_1 = models.ForeignKey(
PreObservation,
related_name="pre_observation_1"
)
pre_observation_standard_2 = models.ForeignKey(
PreObservation,
related_name="pre_observation_2",
blank=True, null=True
)
field_observation_1 = models.ForeignKey(
FieldObservation,
related_name="field_observation_1"
)
field_observation_2 = models.ForeignKey(
FieldObservation,
related_name="field_observation_2",
blank=True, null=True
)
cloud_coverage = models.CharField( max_length=255, choices=CLOUD_COVERAGE )
rain_past_three_days = models.BooleanField( verbose_name="Rain in Past 3 Days" )
snow = models.BooleanField( )
snow_melt = models.FloatField( )
temperature = models.CharField( max_length=255, choices=TEMPERATURE )
wind = models.CharField( max_length=255, choices=WIND )
field_notes = models.TextField( )
teachers_comments = models.TextField( )
user = models.ForeignKey( User )
group_name = models.CharField( max_length=255, blank=True )

You can use
class RecordAdmin(admin.ModelAdmin):
list_display = ('pre_observation__pre_observation_standard_1',
'pre_observation__pre_observation_standard_2', )
admin.site.register(Record, RecordAdmin)

Related

Django: Filtering via SQL, not Python

I created the following context variables context["genders"] and context["ages"].
Currently, there is a lot of work done by Python under #Filtering, while I think it would be better done in #Query.
However, that's where I currently struggle. Do you have an idea on how to achieve the pre-filtering in the #Query section via SQL?
Please not the int(answer_obj.answer) as answer is a TextField.
# Query
responses = Response.objects.filter(
survey__event=12, survey__template=settings.SURVEY_POST_EVENT
).order_by("-created")
# Filtering
filtered_responses = []
for response in responses:
for answer_obj in response.answers.all():
if (
answer_obj.question.focus == QuestionFocus.RECOMMENDATION_TO_FRIENDS
and int(answer_obj.answer) >= 8
):
filtered_responses.append(response)
# Context
gender_list = []
age_list = []
for response in filtered_responses:
for answer_obj in response.answers.all():
# Here a list of all the genders that gave that answer:
if answer_obj.question.focus == QuestionFocus.GENDER:
gender_list.append(answer_obj.answer)
# Here a list of all the ages that gave that answer:
if answer_obj.question.focus == QuestionFocus.AGE:
age_list.append(answer_obj.answer)
context["genders"] = gender_list
context["ages"] = age_list
models.py
class Answer(TimeStampedModel):
question = models.ForeignKey(
"surveys.Question", on_delete=models.CASCADE, related_name="answers"
)
response = models.ForeignKey(
"Response", on_delete=models.CASCADE, related_name="answers"
)
answer = models.TextField(verbose_name=_("Answer"))
choices = models.ManyToManyField(
"surveys.AnswerOption", related_name="answers", blank=True
)
class Response(TimeStampedModel):
class Language(Choices):
CHOICES = settings.LANGUAGES
survey = models.ForeignKey(
"surveys.Survey", on_delete=models.CASCADE, related_name="responses"
)
order = models.ForeignKey(
"orders.Order",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="response",
)
attendee = models.ForeignKey(
"attendees.Attendee",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="response",
)
total_time = models.PositiveIntegerField(
null=True, blank=True, verbose_name=_("Total time")
)
ip_address = models.GenericIPAddressField(null=True, verbose_name=_("IP Address"))
language = models.CharField(
max_length=Language.get_max_length(),
choices=Language.CHOICES,
verbose_name=_("Language"),
)
class Question(TimeStampedModel):
survey = models.ForeignKey(
"surveys.Survey", on_delete=models.CASCADE, related_name="questions"
)
question_set = models.ForeignKey(
"QuestionSet", on_delete=models.CASCADE, related_name="questions"
)
title = models.CharField(max_length=100, verbose_name=_("Title"))
help_text = models.TextField(null=True, blank=True, verbose_name=_("Help text"))
type = models.CharField(
max_length=QuestionType.get_max_length(),
choices=QuestionType.CHOICES,
verbose_name=_("Question type"),
)
focus = models.CharField(
max_length=QuestionFocus.get_max_length(),
choices=QuestionFocus.CHOICES,
verbose_name=_("Question focus"),
)
required = models.BooleanField(default=False, verbose_name=_("Is required?"))
position = models.PositiveSmallIntegerField(
null=True, blank=True, verbose_name=_("Position")
)
# Translatable fields
i18n = TranslationField(fields=("title", "help_text"))
class Meta:
ordering = ("position", "pk")
Filtering
It looks to me, that you want all responses, where answer is higher than eight for question focused on friend recommendation. Is it expected that you might have the same response appended to the filtered responses more than once, or will there be only one question of this type? I think you could rewrite it as follows:
filtered_response = responses.filter(
answers__question__focus=QuestionFocus.RECOMMENDATION_TO_FRIENDS
).annotate(
answer_num=Cast("answers__answer", IntegerField()),
).filter(
answer_num__gt=8,
)
And populating the context:
context["genders"] = Answer.objects.filter(
response_id__in=filtered_response.values_list("id", flat=True),
question__focus=QuestionFocus.GENDER,
).values_list("answer", flat=True)
context["ages"] = Answer.objects.filter(
response_id__in=filtered_response.values_list("id", flat=True),
question__focus=QuestionFocus.AGE,
).values_list("answer", flat=True)
This should allow you to avoid firing the queries to iterate over response.answers.all(), and hopefully achieve the same result.

QuerySet: Filter for entries

I want to show all answers for question__focus=QuestionFocus.REASON_FOR_ATTENDING where question__focus=QuestionFocus.RECOMMENDATION_TO_FRIENDS is >= 9.
The answer field is not an integer field, but a TextField as all questions share the same Answer model. I tried a lot, but nothing worked for me so far.
I first tried to go from the Answer model, but that also didn't work as I filter for different answers than what I want to show at the end.
event = Event.objects.get(pk=12)
survey = event.surveys.get(
template=settings.SURVEY_POST_EVENT
).questions.[HOW TO CONTINUE?]
models.py
class Survey(TimeStampedModel):
class SurveyTemplate(Choices):
CHOICES = ((survey, survey) for survey in settings.SURVEY_TEMPLATES.keys())
id = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4)
event = models.ForeignKey(
"events.Event", on_delete=models.CASCADE, related_name="surveys"
)
is_active = models.BooleanField(default=False, verbose_name=_("Is active?"))
template = models.CharField(
max_length=SurveyTemplate.get_max_length(),
choices=SurveyTemplate.CHOICES,
verbose_name=_("Survey template"),
)
class Response(TimeStampedModel):
class Language(Choices):
CHOICES = settings.LANGUAGES
survey = models.ForeignKey(
"surveys.Survey", on_delete=models.CASCADE, related_name="responses"
)
order = models.ForeignKey(
"orders.Order",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="response",
)
attendee = models.ForeignKey(
"attendees.Attendee",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="response",
)
total_time = models.PositiveIntegerField(
null=True, blank=True, verbose_name=_("Total time")
)
ip_address = models.GenericIPAddressField(null=True, verbose_name=_("IP Address"))
language = models.CharField(
max_length=Language.get_max_length(),
choices=Language.CHOICES,
verbose_name=_("Language"),
)
class Answer(TimeStampedModel):
question = models.ForeignKey(
"surveys.Question", on_delete=models.CASCADE, related_name="answers"
)
response = models.ForeignKey(
"Response", on_delete=models.CASCADE, related_name="answers"
)
answer = models.TextField(verbose_name=_("Answer"))
choices = models.ManyToManyField(
"surveys.AnswerOption", related_name="answers", blank=True
)
class Question(TimeStampedModel):
survey = models.ForeignKey(
"surveys.Survey", on_delete=models.CASCADE, related_name="questions"
)
question_set = models.ForeignKey(
"QuestionSet", on_delete=models.CASCADE, related_name="questions"
)
title = models.CharField(max_length=100, verbose_name=_("Title"))
help_text = models.TextField(null=True, blank=True, verbose_name=_("Help text"))
type = models.CharField(
max_length=QuestionType.get_max_length(),
choices=QuestionType.CHOICES,
verbose_name=_("Question type"),
)
focus = models.CharField(
max_length=QuestionFocus.get_max_length(),
choices=QuestionFocus.CHOICES,
verbose_name=_("Question focus"),
)
required = models.BooleanField(default=False, verbose_name=_("Is required?"))
position = models.PositiveSmallIntegerField(
null=True, blank=True, verbose_name=_("Position")
)

Django query get objects that are not in other object

I have two Django models: Match and MatchRegister. I want to get list of all Matches that are not in MatchRegister object but I'm unable to do so. Could you please help me achieve it?
Below my two classes
class Match(models.Model):
"""Model representing Match object"""
match_number = models.CharField(
max_length=10
)
home_team = models.ForeignKey(
Team,
on_delete=models.SET_NULL,
null=True,
related_name='home_team'
)
away_team = models.ForeignKey(
Team,
on_delete=models.SET_NULL,
null=True,
related_name='away_team'
)
match_category = models.ForeignKey(
MatchCategory,
on_delete=models.SET_NULL,
null=True
)
date_time = models.DateTimeField(
default=timezone.now
)
notes = models.TextField(
max_length=1000,
blank=True
)
last_update = models.DateTimeField(
auto_now=timezone.now
)
class MatchRegister(models.Model):
match = models.ForeignKey(
Match,
on_delete=models.SET_NULL,
null=True
)
You can use the __isnull filter with the correct related_query_name (which defaults to the lower case model name):
Match.objects.filter(matchregister__isnull=True)
You could take list of all the matches connected with a match register and then exclude it from the matches altogether, like this:
all_match_registers = MatchRegister.objects.all()
ids_to_exclude = []
for match_register in all_match_registers:
ids_to_exclude.append(match_register.match.id)
Match.objects.exclude(id__in = ids_to_exclude)

Django drag-n-drop form validation

DJANGO 2.0
So basically I was tasked to make a drag and drop form, in which I would have a table of available users, and create a team from that table, by dragging and dropping the users.
My problem is, that I have no idea where to start tackling this problem, mostly because I'm not sure how to validate the data that is dropped after I hit submit.
So my goals are:
Validate drag-n-drop data
Add a user status so it won't be reassigned to another team.
Assign credits to users upon competition of the given task (this is used for making reports and to add an "achievements" functionalities)
Anything leading to drag-n-drop validation in django will be helpful too
My models.py for users
class Employee(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(
max_length=100,
default=''
)
title = models.CharField(
max_length=60,
default=''
)
date_joined = models.DateField(
default=datetime.date.today
)
avatar = models.ImageField(
upload_to="avatars"
)
My models.py for creating the teams, (I still need to write a Foreign Key or something to store the users that will be on the team):
class Task(models.Model):
Task_name = models.CharField(
max_length=15,
default=""
)
Task_date = models.DateField()
Task_deadline = models.DateField()
Task_credits = models.DecimalField(
max_digits=2,
decimal_places=0
)
Task_reference = models.CharField(
max_length=100,
default=''
)
Task_description = models.TextField(
max_length=3000
)
Task_notes = models.TextField(
max_length=500
)
Task_completition = models.CharField(
max_length=15,
default=_lazy('Assigned')
choices=
)
Task_tags = models.CharField(
max_length=20,
default='',
)

django login users to their portal

I have couple of models and one of them is a foreign key to the user model that's extending django admin. I want to display what belongs to a user in their session upon login. I have defined this authentication that will check whether a particular user exist within the database and redirect them to their session with their instances.
def auth_view(request):
username = request.POST.get('username', '')
password = request.POST.get('password', '')
user = auth.authenticate(username=username, password=password)
if user is not None:
auth.login(request, user)
return HttpResponseRedirect('/studentloggedin/')
Basically, Registration is the first model and a foreign key to Student model, while Student is also a foreign key to UserLog. UserLog is extending the default django admin. I've defined the loggedin session here to filter out details of the individual users upon login.
def studentloggedin(request):
registration = Registration.objects.all()
students = Student.objects.filter(registration=registration)
alluser = UserLog.objects.filter(student=students)
context = {
'registration': registration,
'students': students,
'alluser': alluser,
}
return render(request, "studentloggedin.html", context)
Here is the template rendering the information upon login.
<img
{% for student in students %}
src="{{ student.student_photo.url }}">
<p>{{ student.previous_school }}</p>
{% endfor %}
But I'm getting the below error:
ProgrammingError at /studentloggedin/
more than one row returned by a subquery used as an expression
Just thought to add the models in for your perusal.
class Registration(models.Model):
lastName = models.CharField(
_('Last Name'),
max_length=30,
null=False,
blank=False
)
middleName = models.CharField(
_('Middle Name'),
max_length=30,
null=True,
blank=True
)
firstName = models.CharField(
_('First Name'),
max_length=30,
null=False,
blank=False
)
gender = models.CharField(
_('Gender'),
max_length=30,
choices=GENDER_CHOICES,
default=u' ',
null=False,
blank=False
)
grade = models.CharField(
_('Class'),
max_length=30,
choices=CLASS_CHOICES,
default=u' ',
null=False,
blank=False
)
phone_regex = RegexValidator(
regex=r'^\+?1?\d{9,15}$',
message="Phone number format: '+999999999'. Up to 15 digits allowed."
)
phone_number = models.CharField(
_('Phone Number'),
max_length=255,
validators=[phone_regex],
blank=True
)
email = models.EmailField(
_('Email Address'),
max_length=254,
null=True,
blank=True
)
address = models.CharField(
_('Address'),
max_length=255,
null=False,
blank=False
)
city = models.CharField(
_('City'),
max_length=30,
null=False,
blank=False
)
county = models.CharField(
_('County'),
max_length=30,
choices=COUNTY_CHOICES,
default=None,
null=False,
blank=False
)
nationality = models.CharField(
_('Nationality'),
max_length=30,
null=False,
blank=False
)
dateOfBirth = models.DateField(
_('Date of Birth'),
max_length=30,
null=False,
blank=False
)
placeOfBirth = models.CharField(
_('Place of Birth'),
max_length=255,
null=False,
blank=False
)
regDate = models.DateField(
_('Registration Date'),
max_length=30,
null=False,
blank=False
)
country = models.CharField(
_('Country'),
max_length=255,
null=False,
blank=False
)
emergency = models.CharField(
_('Emergency Contact'),
max_length=255,
null=True,
blank=True
)
emergency_phone = models.CharField(
_('Phone (Emergency Contact)'),
max_length=255,
validators=[phone_regex],
blank=True
)
transcript = models.FileField(
_('Transcript'),
max_length=255,
null=True,
blank=True
)
created = models.DateTimeField(
_('Date Created'),
auto_now=True,
null=True,
blank=True
)
modified = models.DateTimeField(
_('Date Modified'),
auto_now_add=True,
null=False,
blank=False
)
def __str__(self):
return self.firstName
def age(self):
import datetime
return int((datetime.date.today() - self.dateOfBirth).days / 365.25)
def upload_location(instance, filename):
return "%s/%s" % (instance.id, filename)
class Student(models.Model):
import datetime
YEAR_CHOICES = []
for r in range(1980, (datetime.datetime.now().year+1)):
YEAR_CHOICES.append((r, r))
studentID = models.CharField(
_('Student ID'),
max_length=30,
blank=True,
default=''
)
registration = models.ForeignKey(
Registration
)
student_photo = models.ImageField(
_('Picture'),
max_length=255,
null=False,
blank=False,
upload_to=upload_location
)
previous_school = models.CharField(
_('Previous School Attended'),
max_length=255,
null=False,
blank=False
)
previous_school_address = models.CharField(
_('Previous School Address'),
max_length=255,
null=False,
blank=False
)
last_year_attendance = models.IntegerField(
_('Last Year of Attendance'),
choices=YEAR_CHOICES,
default=datetime.datetime.now().year
)
level = models.CharField(
_('Level'),
max_length=255,
choices=LEVEL_CHOICES,
default=None,
null=False,
blank=False
)
enrollment_status = models.CharField(
_('Enrollment Status'),
max_length=255,
choices=ENROLLMENT_CHOICES,
default=None,
null=False,
blank=False
)
enrollment_Date = models.DateField(
_('Enrollment Date'),
max_length=30,
null=False,
blank=False
)
created = models.DateTimeField(
_('Date Created'),
auto_now=True,
null=True,
blank=True
)
modified = models.DateTimeField(
_('Date Modified'),
auto_now_add=True,
null=False,
blank=False
)
class Meta:
ordering = ["-id"]
def __str__(self):
return self.studentID
def save(self, force_insert=False, force_update=False):
if self.studentID == "":
existing_studentIDs = Student.objects.all().order_by('-studentID')
if existing_studentIDs.count() > 0:
new_code = int(existing_studentIDs[0].studentID[1:]) + 1
else:
new_code = 0
self.studentID = 'S%03d' % new_code
super(Student, self).save(force_insert, force_update)
class UserLog(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
student = models.ForeignKey(
Student,
null=True,
blank=True,
default=None
)
staff = models.ForeignKey(
Staff,
null=True,
blank=True,
default=None
)
parent = models.ForeignKey(
Parent,
null=True,
blank=True,
default=None
)
You should query students and alluser this way:
students = Student.objects.filter(registration__in=registration)
alluser = UserLog.objects.filter(student__in=students)
registration and students are multiple objects i.e. queryset. Therefore when querying a foreign key field with multiple objects, we use __in.

Categories

Resources