Store multiple Values in one session variable in django - python

I want to store college IDs in a session variable that each ID should get stored in same session. i want it when user click on ADD button. so whenever the ADD button would be clicked that ID Should get stored in choice session variable.
here is what i have tried.
views.py
def my_choices(request, cid): # when user is making the choices
clg = college.objects.all()
title = "Choice Filling"
page = "Choice Filling"
stud = student.objects.get(id=request.session['id'])
clgid = college.objects.get(id=cid)
choice_list = []
if stud.isactive != 1:
messages.error(request, "Your registration process is incomplete.")
else:
choice_list.insert(len(choice_list), clgid.name)
print(choice_list)
request.session['choices'] = choice_list
return render(request, 'college_list.html', {'cid': cid, 'clg': clg, 'title': title,
'choice':choice_list})
models.py
class college(models.Model):
name = models.CharField(max_length=50)
password = models.CharField(max_length=10)
institute_code = models.IntegerField(unique=True)
class student(models.Model):
fullname = models.CharField(max_length=50)
password = models.CharField(max_length=10)
email = models.EmailField(unique=True)
I want to store cid for every click in choice_list but it's not storing multiple values, instead it overrides the previous value and store new every time. i tried choice_list.insert(..) also but nothing happens.

You're initiating the choice_list as an empty list, instead of loading it from the session
choice_list = []
Replace it with:
choice_list = request.session.get('choices', [])
Your new function should now be
def my_choices(request, cid): # when user is making the choices
clg = college.objects.all()
title = "Choice Filling"
page = "Choice Filling"
stud = student.objects.get(id=request.session['id'])
clgid = college.objects.get(id=cid)
# get choice list from your session
choice_list = request.session.get('choices', [])
if stud.isactive != 1:
messages.error(request, "Your registration process is incomplete.")
else:
choice_list.insert(len(choice_list), clgid.name)
print(choice_list)
request.session['choices'] = choice_list
# the next line shouldn't be required, but if the code still doesnt work, uncomment it
# request.session.modified = True
return render(request, 'college_list.html', {'cid': cid, 'clg': clg, 'title': title,
'choice':choice_list})
Also, if you might have to tell django that the session has been changed. Put this code before returning your view (or after the session edit)
request.session.modified = True

Related

Django - How to get details from many-to-many relationship in multiple select form field

I have 2 models linked with a M2M relationship and I would like to build a form allowing to manage the links but also to add controls on selected values.
This take place outside admin templates.
I managed defining the form to manage links but I was not able to consider additional information to define validity controls.
Here are my models:
class EventGroup(models.Model):
company = models.ForeignKey(
Company, on_delete=models.CASCADE, verbose_name="société"
)
users = models.ManyToManyField(UserComp, verbose_name="utilisateurs", blank=True)
group_name = models.CharField("nom", max_length=100)
weight = models.IntegerField("poids", default=0)
class Event(models.Model):
company = models.ForeignKey(
Company, on_delete=models.CASCADE, verbose_name="société"
)
groups = models.ManyToManyField(EventGroup, verbose_name="groupes", blank=True)
rules = [("MAJ", "Majorité"), ("PROP", "Proportionnelle")]
event_name = models.CharField("nom", max_length=200)
event_date = models.DateField("date de l'événement")
slug = models.SlugField()
current = models.BooleanField("en cours", default=False)
quorum = models.IntegerField(default=33)
rule = models.CharField(
"mode de scrutin", max_length=5, choices=rules, default="MAJ"
)
The form:
class EventDetail(forms.ModelForm):
groups = forms.ModelMultipleChoiceField(
label = "Liste des groupes",
queryset = EventGroup.objects.none(),
widget = forms.CheckboxSelectMultiple,
required = False
)
class Meta:
model = Event
fields = ['event_name', 'event_date', 'quorum', 'rule', 'groups']
The view:
def event_detail(request, evt_id=0):
if evt_id > 0:
current_event = Event.objects.get(id=evt_id)
event_form = EventDetail(request.POST or None, instance=current_event)
else:
event_form = EventDetail(request.POST or None)
company = Company.get_company(request.session['comp_slug'])
event_form.fields['groups'].queryset = EventGroup.objects.\
filter(company=company).\
order_by('group_name')
if request.method == 'POST':
if event_form.is_valid():
event_form.save()
return render(request, "polls/event_detail.html", locals())
This works with no problem to add or remove groups dedicated to the selected or new event, but what I need is to add group's weight information and control that total weight of selected groups is exactly 100. On top of that, I will also need to ensure that each user in the selected groups are listed only once.
Does anybody have any clue on how to implement these controls, or at least how to display additional information to provide the user with related necessary information?
The simplest way to display group weight inside the options would be to change the __str__ method of EventGroup. It would look something like this:
class EventGroup(models.Model):
company = models.ForeignKey(
Company, on_delete=models.CASCADE, verbose_name="société"
)
users = models.ManyToManyField(UserComp, verbose_name="utilisateurs", blank=True)
group_name = models.CharField("nom", max_length=100)
weight = models.IntegerField("poids", default=0)
def __str__(self):
return f"{self.group_name} ({self.weight})"
So a group called 'Group' with weight 75 would appear on the options list as "Group (75)"
If you want to dynamically display total weight of selected options, some javascript will be needed. You'll need a function that's triggered when any option is clicked and somehow finds the weight for that option. Following the __str__ solution, it could look like this:
var current_weight = 0
document.querySelector('#id_groups').addEventListener('click', function(e) {
if (e.target && e.target.nodeName == "LI") {
option = e.target
weight = option.textContent.split('(')
weight = parseInt(weight[-1][:-1])
if (option.checked) {
current_weight -= weight
} else {
current_weight += weight
}
document.getElementById('current_weight').textContent = current_weight
submit_button = document.getElementById('submit')
if (current_weight == 100) {
submit_button.disabled = false
} else {
submit_button.disabled = true
}
})
This will have to be inside a .js file that's imported by your polls/event_detail.html template.
What this script does is listen to clicks on any of the LI elements of django CheckboxMultiple widget and get its weight via some simple string manipulation. The string will be something like "Name of the group (23)". If the option is checked, the script will subtract its weight from current_weight (the user has de-selected the option). Otherwise, it will add the weight. The updated sum will be displayed dinamically on an element you'll add to the event_detail.html template with id='current_weight'. This variable can then control whether the form can be submitted or not, by enabling or disabling its submit button.
If you want server-side validation of this weight == 100 limit, you could do something like that in views.py:
if request.method == 'POST':
total_weight = 0
try:
post = request.POST
for item in post['groups']:
weight = EventGroup.objects.get(pk=item).weight
total_weight += weight
except:
pass
if event_form.is_valid() and total_weight == 100:
event_form.save()
This way instance will be saved only if weight == 100. You can pass an appropriate error message to the user otherwise.
I wasn't able to test this script properly, but I hope the idea helps. I'm not sure what you mean by "ensure that each user in the selected groups are listed only once", so I couldn't answer. Bonne chance!

IntegrityError :(1062, "Duplicate for key 'user_id'")

I am trying to do some job portal kind of thing. I want to that one user can apply to different jobs but not the same one.Here when one user applied to one job he cant apply for another. That's the problem and by using my logic when i running the view i got
'integrityerror'
. Please help me.
Thank You
model.py
class JobsApplied(models.Model):
'''
Events
'''
class Meta:
'''
Meta properties for this model
'''
app_label = 'admin'
verbose_name_plural = 'JobsApplied'
job = models.ForeignKey('Job', db_index=True,related_name='appliedjobs')
user = models.OneToOneField(UserProfile)
create_date = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
'''
Returns object display name
'''
return self.job
views.py
def job_applyview(request):
job_id = request.POST.get('job_id')
## if admin have rights to apply the job please remove "not request.user.is_staff"
# job_id = 11
if request.user and not request.user.is_staff:
job = Job.objects.get(id=job_id)
### please enable below commands after verify
user = UserProfile.objects.get(user__id=request.user.id)
# applied_job = JobsApplied.objects.get(job=job, user=user)
applied_job = JobsApplied.objects.create(job=job, user=user)
applied_job.save(force_insert=True)
message = "you have applied in as " + str(job.job_title)
job_title = Job.objects.get(id=job_id).job_title
industry_id = Job.objects.get(id=job_id)
emp_name = IndustryProfile.objects.get(id=industry_id.industry_id).name
email = IndustryProfile.objects.get(id=industry_id.industry_id).email
b = StudentProfile.objects.get(user_profile=UserProfile.objects.get(user_id=request.user.id))
name = b.name
contact_num = b.mobile_phone
location = b.address_line1
if b.resume:
resume = b.resume.url
else:
resume = ""
body = '''Dear %s, \n The following candidate applied for the job that you have published in Nasscom Jobs Portal.
\n \n Job Title : %s \n name : %s \n Contact_No : %s \n Address : %s \n Resume : %s\n \n Thanks,\n \n Job Portal Support Team''' % (
emp_name, job_title, name, contact_num, location, resume)
email = EmailMessage('Job Applied', body, to=[email])
email.send()
form = JobForm(instance=job)
return render_to_response(
'admin/job.html', {'form': form, 'message': message},
context_instance=RequestContext(request),
)
else:
return redirect('/admin/')
You said:
one user can apply to different jobs
but you implemented as:
class JobsApplied(models.Model):
# [...]
user = models.OneToOneField(UserProfile)
OneToOne field is used when 2 objects are strongly linked together. Here, you say that any instance of JobApplied correspond to a unique user (which seems correct) but you also said any User is linked to 1 (and only 1) JobApplied instance. So in that form, a user cannot apply to 2 or more jobs.
What you want here is a 0..n relationship: you want a user apply to 0 or more Jobs. So you have to declare a ForeignKey in your UserProfile model.

django doesn't save manytomany object

I have this models
class Base(models.Model):
city = models.CharField(max_length=100)
district = models.CharField(max_length=100)
Owner = models.ForeignKey(User, related_name='%(class)s__Game_Owner')
attending = models.ManyToManyField(User)
requested = models.ManyToManyField(User, related_name="%(class)s__requested")
invited = models.ManyToManyField(User, related_name="%(class)s__invited")
teams = models.ManyToManyField(Team)
field = models.CharField(max_length=100)
Hash = models.CharField(max_length=32, blank=True)
and I have other two classes inherting from this base. The class names are
reservation and enlisting
I created this function in the views
def test_join_event(event_id, event_type, user_id):
event = None
if event_type =='enlisting':
event = enlistings.objects.get(id=event_id)
elif event_type =='reservation':
event = reservations.objects.get(id=event_id)
requested_user = User.objects.get(id=user_id)
if requested_user == event.Owner:
return "Error: you are already part of the event"
if requested_user in event.players.all():
return "Error 2!"
if requested_user in event.invited.all():
return "You are already invited to the event!"
if requested_user in event.requested.all():
return "You can't send your request again!"
event.requested.add(requested_user)
event.save()
return "Saved!!"
when I call the function from the interpreter it keeps giving me "Saved" however it should add the user to the requested list and the next time I call the function it returns "You can't send your request again" !!!
I tried the function from the browser it returns saved too, but when I look at the object in the interpreter I see that the requsted list is empty (doesn't have the player I just added)
When I checekd from the interpreter I got the same problem!! I only noticed that I only can add the user who owns that event.
for example like this code
event.requested.add(event.owner)
event.requested.all() # The user is added successfully !!
I can't understand why this is happening. Any ideas??

Display relevant records from many to many in django-tables2

OK, so I have an Item class that has a many-to-many attribute to User through a 'Roles' class. I am trying to create a django-table for the Items such that out of any of the roles attached to the item, if the current User is attached to that role, the name of the role displays. I hope that makes some sort of sense. Here's what I have so far, which I didn't really expect to work because I don't see how the Table class can know about the request/user. I'm stuck.
models.py
class Item(models.Model):
name = models.CharField(max_length=255)
owner = models.ForeignKey(User, related_name='Owner')
roles = models.ManyToManyField(User, through='Role')
class Role(models.Model):
role_type = models.ForeignKey(RoleType)
user = models.ForeignKey(User)
item = models.ForeignKey(Item)
tables.py
class OwnedTable(tables.Table):
roles = tables.Column()
user = request.user
def render_roles(self):
for role in roles:
if role.User == user:
return role.role_type
else:
pass
class Meta:
model = Item
attrs = {"class": "paleblue"}
fields = ('id', 'name', 'owner', 'roles')
You can get the request object from self.context. So if you only need request.user, that's one way to do it.
class OwnedTable(tables.Table):
roles = tables.Column(empty_values=())
def render_roles(self):
user = self.context["request"].user
...
Otherwise, #mariodev's solution works.
It seems like there's no way of using auth user without some overriding.
You can override __init__ for our table class like this:
class OwnedTable(tables.Table):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
super(OwnedTable, self).__init__(*args, **kwargs)
then, inside view, you call table with user argument, like so
table = OwnedTable(Person.objects.all(), user=request.user)
now you can use self.user inside render_roles method to refer to the currently logged in user.
Another solution is shown on https://github.com/bradleyayers/django-tables2/issues/156, which worked for me after some adjustments for my setup.
Given that a Person would have an M2M to Contacts, and you want to display all contacts for that Person in django-tables2, then the following would do:
class PersonTable(tables.Table):
person_contacts = tables.Column(accessor="contacts", verbose_name="Contacts")
def render_person_contacts(self, value, table):
clist = ""
cfirst = True
conts = list(value.all())
for c in conts:
if not cfirst:
clist += "<br />"
else:
cfirst = False
print c.id
uri = reverse('cont_detail', kwargs={'pk': c.id})
clist += '' + c.name + '' + ' (' + c.relation + ')'
return mark_safe(clist)
You basically add a column with a non-existent name, set the accessor to the M2M field name, then call render_<newly_added_column> where you obtain what you need from value.

Django-powered library checkout system

I am working on a library system to manage certain items in our office, I don't need a full-blown integrated library system so I decided to hand roll one with Django.
Below is a simplified version of my model:
class ItemObjects(models.Model):
# Static Variables
IN_STATUS = 'Available'
OUT_STATUS = 'Checked out'
MISSING = 'Missing'
STATUS_CHOICES = (
(IN_STATUS, 'Available'),
(OUT_STATUS, 'Checked out'),
(MISSING, 'Missing'),
)
# Fields
slug = models.SlugField(unique=True)
date_added = models.DateField(auto_now_add=True)
last_checkin = models.DateTimeField(editable=False, null=True)
last_checkout = models.DateTimeField(editable=False, null=True)
last_activity = models.DateTimeField(editable=False, null=True)
status = models.CharField(choices=STATUS_CHOICES, default=IN_STATUS, max_length=25)
who_has = models.OneToOneField(User, blank=True, null=True)
times_out = models.PositiveIntegerField(default=0, editable=False)
notes = models.CharField(blank=True, max_length=500)
history = models.TextField(blank=True, editable=False)
pending_checkin = models.BooleanField(default=False)
pending_transfer = models.BooleanField(default=False)
At first I was using a method on ItemObject to process checking out an item to a user and who_has was an EmailField because I couldn't get a CharfField to populate with the logged in user's name, but I figured using a OneToOneField is probably closer to the "right" way to do this.. While who_has was an EmailField, the following method worked:
def check_out_itemobject(self, user):
user_profile = user.get_profile()
if self.status == 'Available' and self.who_has == '':
self.status = 'Checked out'
self.who_has = user.email
self.last_checkout = datetime.datetime.now()
self.last_activity = datetime.datetime.now()
self.times_out += 1
if self.history == '':
self.history += "%s" % user_profile.full_name
else:
self.history += ", %s" % user_profile.full_name
if user_profile.history == '':
user_profile.history += self.title
else:
user_profile.history += ", %s" % self.title
else:
return False # Not sure is this is "right"
user_profile.save()
super(ItemObjects, self).save()
Now that I am using a OneToOneField this doesn't work, so I started looking at using a subclass of ModelForm but none of the cases I saw here on SO seemed to apply for what I am trying to do; my form would be a button, and that's it. Here are some of the questions I looked at:
Django: saving multiple modelforms simultaneously (complex case)
(Django) (Foreign Key Issues) model.person_id May not be NULL
django update modelform
So was I on the right track with a sort of altered save() method, or would a ModelForm subclass be the way to go?
EDIT/UPDATE: Many thanks to #ChrisPratt!
So I am trying to get Chris Pratt's suggestion for showing ItemHistory to work, but when I try to render it on a page I get an AttributeError that states "'User' object has no attribute 'timestamp'". So my question is, why is it complaining about a User object when last_activity is an attribute on the ItemObject object ?
My view:
#login_required
def item_detail(request, slug):
item = get_object_or_404(Item, slug=slug)
i_history = item.last_activity
user = request.user
return render_to_response('items/item_detail.html',
{ 'item' : item,
'i_history': i_history,
'user' : user })
I do not see why a User object is coming up at this point.
EDIT2: Nevermind, history is clearly a M2M field whose target is User. That's why!
Assuming users will log in and check out books to themselves, then what you most likely want is a ForeignKey to User. A book will only have one User at any given time, but presumably Users could check out other items as well. If there is some limit, even if the limit is actually one per user, it would be better to validate this in the model's clean method. Something like:
def clean(self):
if self.who_has and self.who_has.itemobject_set.count() >= LIMIT:
raise ValidationError('You have already checked out your maximum amount of items.')
Now, you checkout method has a number of issues. First, status should be a defined set of choices, not just random strings.
class ItemObject(models.Model):
AVAILABLE = 1
CHECKED_OUT = 2
STATUS_CHOICES = (
(AVAILABLE, 'Available'),
(CHECKED_OUT, 'Checked Out'),
)
...
status = models.PositiveIntegerField(choices=STATUS_CHOICES, default=AVAILABLE)
Then, you can run your checks like:
if self.status == self.STATUS_AVAILABLE:
self.status = self.STATUS_CHECKED_OUT
You could use strings and a CharField instead if you like, as well. The key is to decouple the static text from your code, which allows much greater flexibility in your app going forward.
Next, history needs to be a ManyToManyField. Right now, your "history" is only who last checked the item out or what the last item the user checked out was, and as a result is pretty useless.
class ItemObject(models.Model):
...
history = models.ManyToManyField(User, through='ItemHistory', related_name='item_history', blank=True)
class ItemHistory(models.Model):
CHECKED_OUT = 1
RETURNED = 2
ACTIVITY_CHOICES = (
(CHECKED_OUT, 'Checked Out'),
(RETURNED, 'Returned'),
)
item = models.ForeignKey(ItemObject)
user = models.ForeignKey(User)
activity = models.PostiveIntegerField(choices=ACTIVITY_CHOICES)
timestamp = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-timestamp'] # latest first
Which then allows you to get full histories:
some_item.history.all()
some_user.item_history.all()
To add a new history, you would do:
ItemHistory.objects.create(item=some_item, user=some_user, activity=ItemHistory.CHECKED_OUT)
The auto_now_add attribute ensures that the timestamp is automatically set when the relationship is created.
You could then actually get rid of the last_checkout and last_activity fields entirely and use something like the following:
class ItemObject(models.Model):
...
def _last_checkout(self):
try:
return self.history.filter(activity=ItemHistory.CHECKED_OUT)[0].timestamp
except IndexError:
return None
last_checkout = property(_last_checkout)
def _last_activity(self):
try:
return self.history.all()[0].timestamp
except IndexError:
return None
last_activity = property(_last_activity)
And, you can then use them as normal:
some_item.last_checkout
Finally, your checkout method is not an override of save so it's not appropriate to call super(ItemObject, self).save(). Just use self.save() instead.

Categories