In my app, I have created rooms where participants(users) can message.
In models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Topic(models.Model):
name = models.CharField(max_length=200)
def __str__(self) :
return self.name
class Room(models.Model):
host = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
topic = models.ForeignKey(Topic, on_delete=models.SET_NULL, null=True)
name = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True)
participants = models.ManyToManyField(User, related_name='participants', blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-updated', '-created']
def __str__(self):
return self.name
class Message(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
room = models.ForeignKey(Room, on_delete=models.CASCADE)
body = models.TextField()
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.body[0:50]
Now, in the participants section, I have added a user whenever the user sends a message sends a message for the first time in that room.
In views.py
def room(request, pk):
room = Room.objects.get(id=pk)
room_messages = room.message_set.all().order_by('-created')
participants = room.participants.all()
print(participants.all())
if request.method == 'POST':
message = Message.objects.create(
user=request.user,
room=room,
body=request.POST.get('body')
)
room.participants.add(request.user)
return redirect('room', pk=room.id)
context = {'room':room, 'room_messages':room_messages,
'participants':participants}
return render(request, 'base/room.html', context)
Now I want to delete a user from participants when that user deletes all his messages from the room. I am not getting an idea how to proceed here.
In views.py
#login_required(login_url='login')
def deleteMessage(request, pk):
message = Message.objects.get(id=pk)
room = Room.objects.get(id=pk)
room_messages = Message.objects.filter(room=room)
if request.user != message.user:
return HttpResponse('You are not allowed here!')
if request.method == 'POST':
message.delete()
return redirect('home')
return render(request, 'base/delete.html', {'obj':message})
How can I do this?
participants = models.ManyToManyField(User, related_name='participants', blank=True)
The related_name here should be rooms, and not participants. This is a pretty common mistake. Although in this case it doesn't affect you.
Now for your problem, you have the Room instance and the User instance, so you can query the Message table and if there are no messages with that User and Room instance combo you can safely remove the user from the room.
messages = Message.objects.filter(room=room, user=request.user).count()
if not messages:
room.participants.remove(request.user)
Related
I have a model form that creates a new job entry, and on submission, I need an invisible field job_time_estimation to be set to a sum of 'service_stats_estimate_duration' values from ServiceItemStats objects associated with the JobEntry by a many-to-many relationship when submitting the form.
For example, if in my NewJobEntryForm I chose two existing ServiceItemStats objects that have service_stats_estimate_duration values 60 and 90, on submission, I want a value 150 to be saved in that JobEntry object's job_time_estimation attribute.
I tried doing this using aggregation by defining a save() method in the model but I am getting an error "name 'serviceItemStats' is not defined".
I am not sure if I am going about this the right way. Any help would be appreciated.
My code:
models.py:
class ServiceItemStats(models.Model):
service_stats_name = models.CharField(primary_key=True, max_length=20)
service_stats_estimate_duration = models.IntegerField()
# Many-to-many relationship with JobEntry.
def __str__(self):
return self.service_stats_name
class JobEntry(models.Model):
# PK: id - automatically assigned by Django.
jo
b_entry_date_time = models.DateTimeField(default=timezone.now)
jo
b_date = models.DateField(blank=True, null=True)
job_checked_in = models.BooleanField()
job_checked_out = models.BooleanField(default=False)
job_priority = models.IntegerField()
job_time_estimation = models.IntegerField(blank=True, null=True)
job_comments = models.TextField(max_length=200, blank=True, null=True)
job_parts_instock = models.BooleanField(default=False)
job_started = models.BooleanField(default=False)
job_finished = models.BooleanField(default=False)
job_expand_fault_evidence = models.ImageField(blank=True, null=True)
job_expand_comments = models.ImageField(blank=True, null=True)
job_expand_parts_required = models.CharField(max_length=200, blank=True, null=True)
vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE) #One-to-one relationship
customer = models.ForeignKey(Customer, on_delete=models.CASCADE) #One-to-one relationship
serviceBay = models.ForeignKey(ServiceBay, on_delete=models.CASCADE, blank=True, null=True) #One-to-one relationship
serviceItemStats = models.ManyToManyField(ServiceItemStats, blank=True) #Many-to-many relationship
def __str__(self):
return self.id
def save(self, *args, **kwargs):
if not self.job_time_estimation:
self.job_time_estimation = serviceItemStats.objects.all().aggregate('service_stats_estimate_duration')
return super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse("jobs:job_detail",kwargs={'pk':self.pk})
views.py
class JobCreateView(FormView):
template_name = "jobs/jobentry_form.html"
form_class = NewJobEntryForm
success_url = reverse_lazy("jobs:job_list")
def form_valid(self, form):
form.save()
return super(job_list, self).form_valid(form)
forms.py
class NewJobEntryForm(ModelForm):
class Meta:
model = JobEntry
fields = ['vehicle', 'customer', 'job_date', 'job_checked_in', 'job_priority', 'job_comments', 'job_parts_instock', 'serviceItemStats']
widgets = {
'job_date' : forms.DateInput(format=('%m/%d/%Y'), attrs={'class':'form-control', 'placeholder':'Select a date', 'type':'date'}),
'ServiceItemStats' : forms.CheckboxSelectMultiple(),
'job_priority' : forms.RadioSelect(choices=priorityOptions),
}
You can try this.
from django.db.models import Sum
class JobCreateView(FormView):
template_name = "jobs/jobentry_form.html"
form_class = NewJobEntryForm
success_url = reverse_lazy("jobs:job_list")
def form_valid(self, form):
job=form.save()
estimation = job.serviceItemStats.all().aggregate(total=Sum('service_stats_estimate_duration'))
job.job_time_estimation = estimation['total']
job.save()
return super(job_list, self).form_valid(form)
I do not want to be able to create multiple Chat objects with the same EXACT participants field.
For example:
If a chat already exists, with participants=["user1", "user2"],
I do not want to be able to create a new chat objects with the same EXACT participants
Looking for something like unique=True, except for manytomanyfield.
Models:
class Contact(models.Model):
user = models.ForeignKey(
User, related_name='friends', on_delete=models.CASCADE)
friends = models.ManyToManyField('self', blank=True)
def __str__(self):
return self.user.username
class Message(models.Model):
contact = models.ForeignKey(
Contact, related_name="messages", on_delete=models.CASCADE)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.contact.user.username
class Chat(models.Model):
participants = models.ManyToManyField(
Contact, related_name='chats')
messages = models.ManyToManyField(Message, blank=True)
def __str__(self):
return f"{self.pk}"
Serializer:
class ContactSerializer(serializers.StringRelatedField):
def to_internal_value(self, value):
return value
class ChatSerializer(serializers.ModelSerializer):
participants = ContactSerializer(many=True)
class Meta:
model = Chat
fields = ("id", "messages", "participants")
read_only = ('id')
def create(self, validated_data):
print(validated_data)
participants = validated_data.pop('participants')
# for c in Chat.participant_set.all():
# print(c)
chat = Chat()
chat.save()
for username in participants:
contact = get_user_contact(username)
chat.participants.add(contact)
chat.save()
return chat
Probably you can try like this:
participants = validated_data.pop('participants')
prev_chat = Chat.objects.all()
for username in participants:
prev_chat = prev_chat.filter(participants__username=username)
if prev_chat.exists():
chat = prev_chat.first()
else:
chat = Chat()
chat.save()
for username in participants:
contact = get_user_contact(username)
chat.participants.add(contact)
I have a django app with which every registered user can create categories. For the authentication I am using django-all-auth. My models.py looks like this:
class Profile(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.OneToOneField(User, on_delete=models.CASCADE)
create_date = models.DateTimeField('date added', auto_now_add=True)
modify_date = models.DateTimeField('date modified', default=timezone.now)
class Category(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
name = models.CharField(max_length=200, unique=True)
create_date = models.DateTimeField('date added', auto_now_add=True)
modify_date = models.DateTimeField('date modified', default=timezone.now)
On the index page the user can see the created categories and create new ones.
The views.py:
def CategoryView(request):
user = 0
if request.user.is_authenticated():
user = request.user
form = CategoryNameForm()
form.user = user
context = {
'categories': Category.objects.all(),
'form': form,
'user':user,
}
if request.method == 'POST':
form = CategoryNameForm(request.POST)
form.user = user
if form.is_valid():
form.save()
return render(request, 'myapp/index.html',context)
forms.py:
class CategoryNameForm(forms.ModelForm):
class Meta:
model = Category
fields = ('name',)
The authentication works. So I was thinking to just put pass the user field into the form :
class CategoryNameForm(forms.ModelForm):
class Meta:
model = Category
fields = ('name','user',)
hide it and then, just select it via JS since the user is in the context. I was just wondering if there is an easier way. This form.user = user for some reason didn't work, I get a NOT NULL constraint failure
There are couple of ways but here is one:
class CategoryNameForm(forms.ModelForm):
class Meta:
model = Category
fields = ('name',) # take out user you don't need it here
def save(self, **kwargs):
user = kwargs.pop('user')
instance = super(CategoryNameForm, self).save(**kwargs)
instance.user = user
instance.save()
return instance
Then in view:
if form.is_valid():
form.save(user=request.user, commit=False)
Make sure your CategoryView is only accessible by authenticated user. Otherwise you will still get NOT NULL constraint failure for user.
I have a trouble working on a ticketing system. Users create a ticket and get the information sent to their email. How can I send new information when a ticket gets changed from the admin panel?
model:
class TroubleTicket(models.Model):
title = models.CharField(max_length=200)
name = models.CharField(max_length=200)
address = models.CharField(choices=ADDRESSES, default=None,
max_length=200)
room = models.CharField(max_length=50)
message = RichTextUploadingField()
state = models.CharField(choices=STATES, max_length=30,
default='New')
answer = RichTextUploadingField(blank=True, null=True)
email = models.CharField(max_length=200, blank=True, null=True)
create_date = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.title
def get_absolute_url(self):
return reverse('ticket_detail', kwargs={'pk': self.id})
class Meta:
ordering = ['-create_date']
save form and sending a message if the user specified a mail:
class CreateTicket(CreateView):
template_name = 'incidentjournal/add_ticket.html'
form_class = TicketForm
model = TroubleTicket
context_object_name = 'ticket'
success_url = reverse_lazy('home')
def form_valid(self, form):
recipient = form.cleaned_data['email'].encode('utf8')
new_ticket = form.save()
new_ticket_id = new_ticket.pk
address = new_ticket.address.encode('utf8')
title = new_ticket.title.encode('utf8')
name = new_ticket.name.encode('utf8')
room = new_ticket.room.encode('utf8')
send_to_email(recipient, str(new_ticket_id), title, address,
room, name)
send_to_bot(str(new_ticket_id), title, address, room, name)
return super(CreateTicket, self, ).form_valid(form)
If the state or the answer has been changed in the admin panel I want to send the changes to the user.
Thanks for your advice.
Im trying create a new book and review from on request.POST. The issue here is the issue is that the data needs to go to two models with foreign keys. Here is the request.POST:
def add(request):
if request.method == 'POST':
result = Review.objects.addBook_and_Review(
user=request.session['id'],
title=request.POST['title'],
author=request.POST['author'],
new_author=request.POST['new_author'],
review=request.POST['review'],
rating=request.POST['rating']
)
return redirect('add')
else:
return render(request, 'books/add.html')
And here is the the custom manager and the two models (Review and Book). Note the Review model with foreign Keys.
class ReviewManager(models.Manager):
def addBook_and_Review(self, **kwargs):
#custom manager code here
return True
class BookManager(models.Manager):
print('hit book manager')
pass
class User(models.Model):
name = models.CharField(max_length=200)
alias = models.CharField(max_length=200)
email = models.EmailField()
pw_hash = models.CharField(max_length=200)
created_at = models.DateField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
objects = UserManager()
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.CharField(max_length=200)
created_at = models.DateField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
objects = BookManager()
class Review(models.Model):
review = models.CharField(max_length=1000)
rating = models.CharField(max_length=200)
created_at = models.DateField(auto_now_add=True)
updated_at = models.DateField(auto_now=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
book = models.ForeignKey(Book, on_delete=models.CASCADE)
objects = ReviewManager()
It's easy. If you want to create Book and Review objects using one POST request, do the following (continuing your code):
def add(request):
if request.method == 'POST':
book = Book.objects.create(
title=request.POST['title'],
author=request.POST['author'],
)
result = Review.objects.addBook_and_Review(
user=request.session['id'],
title=request.POST['title'],
author=request.POST['author'],
new_author=request.POST['new_author'],
review=request.POST['review'],
rating=request.POST['rating'],
book=review)
return redirect('add')
else:
return render(request, 'books/add.html')
It is recommended that you do this after the validation using forms.