django doesn't save manytomany object - python

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??

Related

How to realize the hard delete function in Python Django?

I have a User realization class, in there I want to have two type delete function:
class User(AbstractUser):
nickname = models.CharField(max_length=16, blank=True, null=True) # nickname
real_name = models.CharField(max_length=12, null=True, blank=True)
phone = models.CharField(max_length=18) # telephone
...
status = models.CharField(max_length=1, null=True, default=1)
def soft_del(self):
self.status = 4
return True
def hard_delete_user(self):
# what should I do there?
return True
you see, one is soft delete a user, the other is hard delete a user.
I mean the soft delete, a user still in the database, but I will not query it by my code, the hard delete is delete it from the database table.
How to realize the hard_delete_user function?
This is just self.delete() [Django-doc], given I understood you correctly:
class User(AbstractUser):
# ...
def hard_delete_user(self):
self.delete()
return True
You might want to restrict this function, for example only delete the User object, given it was already softly deleted, like:
class User(AbstractUser):
# ...
def hard_delete_user(self):
# example: restrict deletion to softly deleted
if self.status == 4:
self.delete()
return True
return False

Django: Cross referencing Model objects within a model - best practise?

I'm currently going around in a bit of a "QuerySet' object has no attribute '' <> app.models.DoesNotExist: Messages matching query does not exist loop.
Essentially, I'm trying to define "last_activity" on a Room model that is referencing the time at which the last Message associated to that room was sent. This is my attempt:
class Room(models.Model):
title = models.CharField(max_length=255)
staff = models.BooleanField(default=False)
slug = models.SlugField(max_length=250, default='')
banner = models.ImageField(storage=USER_UPLOAD_LOC, null=True, blank=True)
def last_activity(self):
last_persisted_message = Messages.objects.filter(where=self.title).order_by('-sent_at')[:1]
return last_persisted_message.sent_at
class Messages(models.Model):
room = models.ForeignKey(Room, on_delete=models.CASCADE)
where = models.CharField(max_length=255)
message = models.TextField(default='')
user = models.ForeignKey(settings.AUTH_USER_MODEL)
username_from = models.CharField(max_length=255)
username_to = models.CharField(max_length=255, default='all')
sent_at = models.DateTimeField(default=datetime.datetime.now)
I've tried so many things now and referenced the query set documentation and nothing seems to be working.
I can also confirm that the "where" field for the Messages model is populated when a message is created by {{ room.title }}. I'm using a web socket connection on the client side to pass a "message" back to the websocket consumer.py which then persists the message to the DB).
If you slice the queryset with [:1] you will obtain a queryset containing at most one item, but not an item itself. You can use [0] to obtain the first item, or, .first():
def last_activity(self):
last_persisted_message = Messages.objects \
.filter(where=self.slug) \
.order_by('-sent_at') \
.first()
if last_persisted_message is not None:
return last_persisted_message.sent_at
If you use [0] and there is no such item, then the program will raise an IndexError (since there is no item with index 0).
In case there is no such Messages object (that satisfies the filter(..)), then last_persisted_message will be None, so you have to find a way to resolve that case. Here we return None in that case.

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.

Send an email once object meets certain criteria

[Hopefully I entitled this post correctly]
I have a (sort of) 'follow' twitter thing going on. Users can follow a company profile object, which creates a follower object.
class Follower(models.Model):
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
profile = models.ForeignKey(UserProfile)
company = models.ForeignKey(Company)
verified = models.BooleanField(default=False)
from_user = models.BooleanField(default=False)
...
class Company(models.Model):
owner = models.ForeignKey(User, null=True, blank=True, on_delete=models.SET_NULL)
name = models.CharField(max_length=200)
... and more fields that aren't relevant
What I'd like to do is send an update email to the company profile owner, after 5 new followers. 'You have 5 new followers!'.
As of right now I'm sending an email to the owner every time they get a new follower. A little much, I know.
I'm guessing I need to create a list of followers, send it, and then delete it to prepare for 5 new followers? I'm really not sure how to go about this. Any help or suggestions are greatly appreciated.
view:
#login_required
# this is creating a follower. Maybe I shouldn't send the email through this?
def follow(request, id):
company = Company.objects.get(id=id)
profile = get_object_or_404(UserProfile, user__username=request.user.username)
try:
follower = Follower.objects.get(profile=profile, company=company)
if not follower.verified:
follower.verified = True
follower.save()
messages.success(request, 'Now following %s\''%company.name)
mes = Message(subject="New Follower", sender=profile, recipient=company.owner)
mes.body = render_to_string('emails/email-message/new_follower.html', RequestContext(request, {
'sender': profile,
'receiver': company.owner,
}))
except ObjectDoesNotExist:
messages.error(request, 'Failed to follow.')
Send the email every time the number of followers for a specific company becomes a multiple of 5, like so:
if not (Follower.objects.filter(company=company).count() % 5):
#send the email

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