I am having an issue with querying an attribute from a custom class connected with the user class.
My Goal: I want to send messages on my website per mouseclick via telegram bot to my phone (messaging works) - For this I need the USERĀ“S CHAT ID
As there are multiple users I stored the chat_id in a new model I created, which is linked to User model with a onetoonefield:
User = get_user_model()
class TelegramProfile(models.Model):
name = models.CharField(default="",max_length=200,blank=True,unique=True)
user = models.OneToOneField(User,on_delete=models.CASCADE,related_name="user_id")
telegram_chat_id = models.CharField(max_length=40,default="",editable=True,blank=True,unique=True)
def __str__(self):
return str(self.name)
My Usermodel is the built in Model:
class User(auth.models.User, auth.models.PermissionsMixin):
def __str__(self):
return f"#{self.username}"
So in my views.py file I have the function on which I grab the message (another model class called Recipe - I identify it by a primary key) and and the chat_id which belongs to a specific user:
def bot(request,msg,chat_id,token=my_token):
bot=telegram.Bot(token=token)
bot.send_message(chat_id=chat_id, text=msg)
def home(request,pk):
recipe = get_object_or_404(Recipe,pk=pk)
user = request.user
chat_id = User.objects.get(TelegramProfile.telegram_chat_id,user) #with this query I try to grab the chat_id of the logged in user
ingredients = recipe.ingredients
ingredients = ingredients.split("<p>")
ingredients = "\n".join(ingredients)
ingredients = strip_tags(ingredients)
bot(request,ingredients,chat_id)
return render(request,"recipes/send_recipe.html")
So my question is:
-How do I make the query for the chat_id so that:
the chat_id of the specific logged in User will be called so I can insert it in the bot function to send the message to this specific id?
I am still new in django so I am still learning the queries, thanks so much in advance!!!
EDIT: Was able to solve it myself:
def send_recipe(request, pk):
try:
if request.method == "POST":
data_option = str(request.POST[
"testselect"]) # testselect is the select form in recipe_detail.html with which I select 1 of the 2 options
if data_option == "Ingredients":
recipe = get_object_or_404(Recipe, pk=pk)
recipe_name = recipe.name.upper()
ingredients = recipe.ingredients
ingredients = ingredients.split("<p>")
ingredients = "\n".join(ingredients)
ingredients = strip_tags(ingredients)
message = f"{recipe_name}: \n {ingredients}"
user = TelegramProfile.objects.get(
user=request.user) # use get instead of filter! filter returns the name but can t use it as object, with get I get the object and I can use the attributes on it!
chat_id = user.telegram_chat_id
bot(request, message, chat_id)
return render(request, "recipes/send_recipe.html")
elif data_option == "Ingredients & Description":
recipe = get_object_or_404(Recipe, pk=pk)
recipe_name = recipe.name.upper() # recipe name
ingredients = recipe.ingredients
ingredients = ingredients.split("<p>")
ingredients = "\n".join(ingredients)
ingredients = strip_tags(
ingredients) # we grab recipes ingredients and mute them, split at p tag, then add a new line and join them then remove tags
recipe_description = recipe.description
recipe_description = recipe_description.split("<p>")
recipe_description = "\n".join(recipe_description)
recipe_description = strip_tags(recipe_description)
message = f"{recipe_name}: \n {ingredients} \n \n {recipe_description}"
user = TelegramProfile.objects.get(user=request.user)
chat_id = user.telegram_chat_id
bot(request, message, chat_id)
return render(request, "recipes/send_recipe.html")
else:
return render(request, "recipes/create_telegram.html")
except Exception:
return render(request, "recipes/create_telegram.html")
Related
I'm trying to implement a search feature where user's can search an email on a React frontend and it'll return that email's top 5 love languages. Currently the url path requires the primary key of a love language model, but I want it to use the user id. I have this Django URL set as:
path('love-languages/', LovesView.as_view(), name='love-languages'),
path('love-languages/<int:pk>', LoveView.as_view(), name='love-languages')
Relevant love language model:
class Love(models.Model):
# Obtaining the user from the user model
user = models.ForeignKey(
get_user_model(),
on_delete = models.CASCADE
)
# Defining the dropdown choices
class LoveLanguages(models.TextChoices):
ACTS_OF_SERVICE = 'Acts of Service'
RECEIVING_GIFTS = 'Receiving Gifts'
QUALITY_TIME = 'Quality Time'
WORDS_OF_AFFIRMATION = 'Words of Affirmation'
PHYSICAL_TOUCH = 'Physical Touch'
one = models.CharField(max_length=20, choices=LoveLanguages.choices)
two = models.CharField(max_length=20, choices=LoveLanguages.choices)
three = models.CharField(max_length=20, choices=LoveLanguages.choices)
four = models.CharField(max_length=20, choices=LoveLanguages.choices)
five = models.CharField(max_length=20, choices=LoveLanguages.choices)
and love language views:
class LovesView(APIView):
def get(self, request):
loves = Love.objects.filter(user=request.user.id)
data = LoveSerializer(loves, many=True).data
return Response(data)
def post(self, request):
request.data['user'] = request.user.id
love = LoveSerializer(data=request.data)
if love.is_valid():
love.save()
return Response(love.data, status=status.HTTP_201_CREATED)
else:
return Response(love.errors, status=status.HTTP_400_BAD_REQUEST)
class LoveView(APIView):
def get(self, request, pk):
love = get_object_or_404(Love, pk=pk)
# if request.user != love.user:
# raise PermissionDenied('Unauthorized, you are not signed in as this user')
# else:
data = LoveSerializer(love).data
return Response(data)
def delete(self, request, pk):
love = get_object_or_404(Love, pk=pk)
if request.user != love.user:
raise PermissionDenied('Unauthorized, you are not signed in as this user')
else:
love.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
def put(self, request, pk):
love = get_object_or_404(Love, pk=pk)
if request.user != love.owner:
raise PermissionDenied('Unauthorized, you are not signed in as this user')
else:
updated_love = LoveSerializer(love, data=request.data)
if updated_love.is_valid():
updated_love.save()
return Response(updated_love.data)
else:
return Response(love.errors, status=status.HTTP_400_BAD_REQUEST)
So, if this were the data returned:
{
"id": 4,
"user": 2,
"one": "Acts of Service",
"two": "Receiving Gifts",
"three": "Quality Time",
"four": "Words of Affirmation",
"five": "Physical Touch"
}
The URL would have to be .../love-languages/4. I want the URL to be .../love-languages/2 because I am not sure how I can access the pk of 4 when only an email is entered. I've thought about referencing the love language model in the user model, but I think there should be a way to replace <int:pk> in the URL with something like <int:user>? I tried that and it did not work, I got an error saying TypeError: get() got an unexpected keyword argument 'user' I was reading the django docs for URL dispatcher but couldn't find anything useful at the moment.
I also tried changing the get request in the views to
love = get_object_or_404(Love, pk=request.user.id)
but that didn't work. It only returns the same data regardless of what id is entered in the url.
If I understand the question correctly here is what you need to do.
Django allows you to query a model and its relationship with __.
So to make a query about Love model with respect to user you would do something like this.
try:
love = Love.objects.get(user__email=variable_email)
except Love.DoesNotExist:
raise Http404
there are 2 ways to detect email in url.
you can simply use <str:variable_email> and then clean the email in you view before using it to query your database.
or you can use regex to detect in url pattern.( like question asked here)
This is probably very simple and basic but I'm struggling with grabbing a newly-created object in Django. It is for a basic library-style app. Over in models, I do this to create a Book object:
def add_book(self, postData, user_id):
title = postData['title']
first_name = postData['first_name']
last_name = postData['last_name']
user_obj = User.objects.get(id=user_id)
if not Author.objects.filter(first_name=first_name, last_name=last_name).exists():
author_obj = Author.objects.create(first_name=first_name, last_name=last_name)
else:
author_obj = Author.objects.get(first_name=first_name, last_name=last_name)
return self.create(title=postData['title'], created_by=user_obj, author=author_obj)
Then in views, I call that method and wish to redirect to a page specifically for that newly-created object. I think you can see that I have most of the code down, but don't know what to put in place of the "????".
def books_add(request):
if request.method == "POST":
errors = Book.objects.book_validation(request.POST)
if not errors:
Book.objects.add_book(request.POST, request.session['uid'])
book_id = Book.objects.get(????).id
return redirect('/books/book/{}/'.format(book_id))
else:
context = {
'errors' : errors,
}
1st part use get_or_create for retrieve or create a model entry
def add_book(self, postData, user_id):
title = postData['title']
first_name = postData['first_name']
last_name = postData['last_name']
user_obj = User.objects.get(id=user_id)
author_obj, created = Author.objects.get_or_create(first_name=first_name, last_name=last_name)
return self.create(title=postData['title'], created_by=user_obj, author=author_obj)
2nd part, return self.create return a Book entity :
def books_add(request):
if request.method == "POST":
errors = Book.objects.book_validation(request.POST)
if not errors:
book = Book.objects.add_book(request.POST, request.session['uid'])
return redirect('/books/book/{}/'.format(book.id))
else:
context = {
'errors' : errors,
}
There are some issues here. At the very least, look at Django Forms before you go much further. This is what a view that creates an object could look like:
def add_book(request):
if request.POST:
author, created = Author.objects.get_or_create(first_name=first_name,
last_name=last_name)
book = Book(title = request.POST['title'],
user_obj = request.GET['user'],
author = author,)
book.save()
return redirect('/books/book/{}/'.format(book.id))
else:
return render(request, 'book_form.html')
You really need to look into ModelForms to handle your POSTs. But start with looking at Forms.
I'm trying to create a django app in which one user can add other user as Friend. Here's what I did,
models.py,
class Friend(models.Model):
users = models.ManyToManyField(User)
current_user = models.ForeignKey(User, related_name='all', null=True)
views.py
# view for adding or removing friends
def change_friends(request, pk):
new_friend = User.objects.get(pk=pk)
data = Friend.objects.get(current_user=request.user)
frnds = data.users.all()
new_friend in frnds:
data.users.remove(new_friend)
else:
data.users.add(new_friend)
redirect(request.META['HTTP_REFERER'])
# Displaying frinends,
def following(request, id=None):
my_friend, created = Friend.objects.get_or_create(current_user_id=id)
all_friends = my_friend.users.all()
return render(request, 'all/follow.html', {'all_friends': all_friends})
This code was working fine until I added friends from 1 account only, but when I added several friends from several accounts it started showing an error get() returned more than one Friend -- it returned 2!.
How can we fix that? Thank You!
in change friend function this line of code change like this
new_friend = User.objects.filter(pk=pk).first()
Try this...
Delete all Friend instances in admin and change model to:
class Friend(models.Model):
users = models.ManyToManyField(User)
current_user = models.OneToOne(User, related_name='friend', on_delete=models.CASCADE, null=True)
then views should be:
# view for adding or removing friends
def change_friends(request, pk):
new_friend = User.objects.get(pk=pk)
friends = request.user.friend.users.all()
new_friend in friends:
request.user.users.remove(new_friend)
else:
request.user.users.add(new_friend)
redirect(request.META['HTTP_REFERER'])
# Displaying frinends,
def following(request, id=None):
my_friend, created = Friend.objects.get_or_create(current_user_id=id)
all_friends = my_friend.users.all()
return render(request, 'all/follow.html', {'all_friends': all_friends})
If you use many_to_many to record friends relationship,it should be better set model as:
current(OneToOne) users(ManyToMany)
If you use ForeignKey to record friends relationship,it should be better set model as:
current(ForeignKey) user(ForeignKey)
Update
If you can't change models anymore,just change code to:
def change_friends(request, pk):
new_friend = User.objects.get(pk=pk)
data = Friend.objects.filter(current_user=request.user)
has_user = False
for x in data:
if new_friend in x.users.all():
has_user = True
x.users.remove(new_friend)
if not has_user:
firend = Friend.objects.filter(current_user=request.user).first()
if friend:
friend.users.add(new_friend)
else:
friend = Friend.objects.create(current_user=request.user)
friend.users.add(new_friend)
redirect(request.META['HTTP_REFERER'])
I want to implement a referral system to my system where registered users can invite other people by emailing their referral link (i.e. /register/referral/123123/) just like dropbox one ( and if a person signs up, the referrer gets additional bonus.
Currently I have implemented it this way:
Models:
class UserReferral(models.Model):
STATUS_INVITED = 1
STATUS_ACCEPTED = 2
STATUS_EXPIRED = 3
STATUS_CHOICES = (
(STATUS_INVITED, 'Invited'),
(STATUS_ACCEPTED, 'Accepted'),
(STATUS_EXPIRED, 'Expired'),
)
referrer = models.ForeignKey(User, related_name='referrers')
referred = models.ForeignKey(User, related_name='referred')
number = models.IntegerField()
status = models.IntegerField(choices=STATUS_CHOICES, default=STATUS_INVITED)
class Meta:
unique_together = (('referrer', 'referred'),)
def __unicode__(self):
return 'User %s referred %s' % (self.referrer.get_full_name(), self.referred.get_full_name())
#property
def referral_expired(self):
expiration_date = datetime.timedelta(days=settings.ACCOUNT_ACTIVATION_DAYS)
return (self.status == self.STATUS_ACCEPTED or
(self.referred.date_joined + expiration_date <= datetime_now()))
Views:
This view is used by the registered users to send out new referral invites
#login_required
def invite_friends(request, template_name='accounts/invite_friends.html'):
if request.method == 'POST':
form = InviteForm(request.POST, user=request.user)
if form.is_valid():
emails = form.cleaned_data['emails']
for email in emails:
try:
user_referral = UserReferral.objects.get(referrer=request.user, referred__email=email)
except UserReferral.DoesNotExist:
random_username = ''.join(random.choice(string.ascii_uppercase) for x in range(6))
user = User.objects.create(username=random_username, password=email, is_active=False) # Dummy user to be overridden
user_referral = UserReferral.objects.create(referrer=request.user, referred=user, number=random.randint(10000, 99999))
send_mail('accounts/notifications/invite_friends', recipient_list=[email],
context={'user': request.user, 'number': user_referral.number})
messages.add_message(request, messages.SUCCESS, "Invites are sent.")
return redirect(reverse('profile_dashboard'))
else:
form = InviteForm(user=request.user)
return render(request, template_name, locals())
This is the URL where referred users can register, it basically calls the original register function with referral code, and check in the register view if the referral code is present, if so, it fetches the referred user instance from the UserReferral instance and populates the user data from the register form and saves that new user.
def referred_signup(request, referral_code):
user_referral = get_object_or_404(UserReferral, number=referral_code)
if user_referral.referral_expired:
raise Http404
response = register(request, referral_code=referral_code)
return response
So I create the dummy inactive 'referrer' User account every time the new invite is generated. And when on the registration time, I populate the names, password etc. from the user input form, and change the UserReferral instance status to ACTIVATED. Is there any better alternative to this one?
I have a model for event invitations:
class EventInvitation(models.Model):
from_user = models.ForeignKey(User, related_name="inviters")
to_user = models.ForeignKey(User, related_name="invited")
text = models.CharField(max_length= 150)
event = models.ForeignKey(Event)
sent = models.DateTimeField(auto_now=True)
status = models.IntegerField(choices=INVITATION_STATI, default=0)
What I am trying to accomplish is to have a dynamic invitation form (NOT ModelForm), where the inviter has a selection of users, based on a query / people who CAN be invited by that particular user, and a selection of groups, also based on a query (the inviter has to be the owner of the group)... Since the EventInvitation is for a single user, I would then iterate through the selected users AND members of the group and create individual invitations for all of them. Any idea how I could generate this dynamic form?
As I understand, you want a logged in user, from an event page, click on a "Invite Others" button, which shows him a form, where he can select specific users and groups, specify some text, and click on "Send". Then your app should create many instances of the invitation (one per user) and send them, tracking the status of invitation. If this is correct, here are my suggestions for implementing this:
Using the following models will give you more control over your data (keeping the text only once, and allowing you to look up all users for a specific invitation):
class EventInvitation(models.Model):
inviter = models.ForeignKey(User, related_name="inviters")
event = models.ForeignKey(Event)
text = models.CharField(max_length= 150)
created = models.DateTimeField(auto_now=True)
class EventInvitationInvitee(models.Model):
event_invitation = models.ForeignKey(EventInvitation)
user = models.ForeignKey(User, related_name="invited")
status = models.IntegerField(choices=INVITATION_STATI, default=0)
Use a simple form like this:
from django.contrib.auth.models import User, Group
class InviteForm(forms.Form):
text = forms.CharField(widget=forms.Textarea)
users = forms.ModelMultipleChoiceField(queryset=User.objects.all())
groups = forms.ModelMultipleChoiceField(queryset=Group.objects.all())
def __init__(self, user, *args, **kwargs):
super(InviteForm, self).__init__(*args, **kwargs)
self.fields['users'].queryset = User.objects.filter( ... )
self.fields['groups'].queryset = User.objects.filter( ... )
Replace ... with your code for filtering the correct groups and users.
And a view in this fashion:
def invite(request, event_id):
event = get_object_or_404(Event, pk=event_id) # you can check if your user is allowed to access this event here
if request.method == 'POST':
form = InviteForm(request.user, request.POST)
if form.is_valid():
invitation = EventInvitation.objects.create(inviter=request.user, event=event, text = form.cleaned_data['text'])
users = set()
for user in form.cleaned_data['users']:
users.add(user)
EventInvitationInvitee.objects.create(event_invitation=invitation, user=user)
for group in form.cleaned_data['groups']:
for user in group.user_set.all():
if user not in users:
users.add(user)
EventInvitationInvitee.objects.create(event_invitation=invitation, user=user)
return HttpResponseRedirect('/thanks/') # Redirect after POST
else:
form = InviteForm(request.user)
return render_to_response('invite.html', {'form': form})
Update: You can also create a dynamic form in a more pythonic way like this:
def get_invite_form(user):
class InviteForm(forms.Form):
text = forms.CharField(widget=forms.Textarea)
users = forms.ModelMultipleChoiceField(queryset= ... )
groups = forms.ModelMultipleChoiceField(queryset= ... )
return InviteForm
replacing ... with a queryset using the user parameter, and later use get_invite_form(request.user)(request.POST) and get_invite_form(request.user)() instead of InviteForm().