SQL statement check if user is allowed - mysql

I have the following three tables:
templates <-----> templatesubscription <-----> user
table template:
id | name
table user
id | name | ...
table templatesubscriptions
id | template_id | user_id
Each user can have n template subscriptions.
Each template subscription can have 1 user.
Each template has n template subscriptions.
Each template subscription has 1 template.
Now I want to check if the currently logged in user (User ID = 10) can edit the template with the ID = 0. Only if the user has an template subscription for this template he can edit it.
Now I could make 2 queries. First I will get the template subscription of the current user with the template id:
SELECT * FROM templatesubscription WHERE user_id = 10 AND template_id = 0
Now I can check if the return value is null, if yes the user will get an error message. If the result isn't empty, I will get the template of the template table:
SELECT * FROM templates WHERE id = 0
But this approach doesn't seems really nice to me. Can I combine both statements in one querie? The return value should be the template or null if the user has no template subscription for this template.

You can have them both together using a where condition. Simply there is output if the user has permission.
SELECT *
FROM templates
WHERE id = 0 and 1=(SELECT 1 FROM templatesubscription WHERE user_id = 10 AND template_id = 0)

Learn about joins, here's a good overview.
So in your case, you want to inner join between subscriptions and templates, to only get the subscriptions the user has access to:
SELECT t.* -- it's better to list all the columns than use *
FROM templates t
INNER
JOIN templatesubscription ts
ON t.id = ts.template_id
WHERE ts.user_id = 10
AND ts.template_id = 0
So this will return the row(s) from templates table only if there is a record in templatesubscription for that user_id.

Related

Querying MySQL result set for existence of a particular value

MySQL 8.x here. I have the following tables:
[users] (users in the system)
===
user_id
user_username
user_password_enc
user_first_name
user_last_name
[events] (CRUDdable things the users can participate in)
===
event_id
event_name
event_date
event_location
[event_roles] (coarse-grained, event-specific roles that users can be assigned to)
===
event_role_id
event_role_name
event_role_label -- ex: EVENT_ADMIN, EVENT_REP, EVENT_FOLLOWER
[event_permissions] (granular permissions that get assigned to roles)
===
event_permission_id
event_permission_name
event_permission_label -- ex: CAN_VIEW_LOCATION, CAN_CHANGE_DATE, CAN_CHANGE_LOCATION, CAN_CANCEL_EVENT
[event_permissions_x_roles] (crosswalk table mapping permissions to roles)
===
event_permissions_x_role_id
event_permission_id
event_role_id
[event_user_roles] (which users are assigned to which event roles)
===
event_user_role_id
event_id
user_id
event_role_id
I now want to write a query that asks the following question:
Does user_id = 234 have CAN_CHANGE_LOCATION permission to event_id=123?
My best attempt thus far:
SELECT
event_permission_label
FROM
event_permissions_x_roles
INNER JOIN
event_permissions
ON
event_permissions_x_roles.event_permission_id = event_permissions.event_permission_id
WHERE
event_role_id = (
SELECT
event_role_id
FROM
event_user_roles
WHERE
user_id = 234
AND
event_id = 123
);
However this will only give me a list of event_permission_labels that the user_id=234 has for event_id=123.
That's close to what I want, but I want my query to go further and check that list to see if CAN_CHANGE_LOCATION is part of that list. I don't know if there's a way to write this so that it returns a boolean true/false (yes user does have the desired permission for the given event; or no the user does not, etc.). Can anyone spot where I'm falling a bit short?
Schematically:
SELECT EXISTS ( SELECT NULL
FROM {joined tables set}
WHERE t1.user_id = 234
AND t2.event_permission_label = 'CAN_CHANGE_LOCATION'
AND t3.event_id = 123 )

Django querysets: Excluding NULL values across multiple joins

I'm trying to avoid using extra() here, but haven't found a way to get the results I want using Django's other queryset methods.
My models relationships are as follows:
Model: Enrollment
FK to Course
FK to User
FK to Mentor (can be NULL)
Model: Course
FK to CourseType
In a single query: given a User, I'm trying to get all of the CourseTypes they have access to. A User has access to a CourseType if they have an Enrollment with both a Course of that CourseType AND an existing Mentor.
This user has 2 Enrollments: one in a Course for CourseType ID 6, and the other for a Course for CourseType ID 7, but her enrollment for CourseType ID 7 does not have a mentor, so she does not have access to CourseType ID 7.
user = User.objects.get(pk=123)
This works fine: Get all of the CourseTypes that the user has enrollments for, but don't (yet) query for the mentor requirement:
In [28]: CourseType.objects.filter(course__enrollment__user=user).values('pk')
Out[28]: [{'pk': 6L}, {'pk': 7L}]
This does not give me the result I want: Excluding enrollments with NULL mentor values. I want it to return only ID 6 since that is the only enrollment with a mentor, but it returns an empty queryset:
In [29]: CourseType.objects.filter(course__enrollment__user=user).exclude(course__enrollment__mentor=None).values('pk')
Out[29]: []
Here's the generated SQL for the last queryset that isn't returning what I want it to:
SELECT `courses_coursetype`.`id` FROM `courses_coursetype` INNER JOIN `courses_course` ON ( `courses_coursetype`.`id` = `courses_course`.`course_type_id` ) INNER JOIN `store_enrollment` ON ( `courses_course`.`id` = `store_enrollment`.`course_id` ) WHERE (`store_enrollment`.`user_id` = 3877 AND NOT (`courses_coursetype`.`id` IN (SELECT U0.`id` AS `id` FROM `courses_coursetype` U0 LEFT OUTER JOIN `courses_course` U1 ON ( U0.`id` = U1.`course_type_id` ) LEFT OUTER JOIN `store_enrollment` U2 ON ( U1.`id` = U2.`course_id` ) WHERE U2.`mentor_id` IS NULL)))
The problem, it seems, is that in implementing the exclude(), Django is creating a subquery which is excluding more rows than I want excluded.
To get the desired results, I had to use extra() to explicitly exclude NULL Mentor values in the WHERE clause:
In [36]: CourseType.objects.filter(course__enrollment__user=user).extra(where=['store_enrollment.mentor_id IS NOT NULL']).values('pk')
Out[36]: [{'pk': 6L}]
Is there a way to get this result without using extra()? If not, should I file a ticket with Django per the docs? I looked at the existing tickets and searched for this issue but unfortunately came up short.
I'm using Django 1.7.10 with MySQL.
Thanks!
Try using isnull.
CourseType.objects.filter(
course__enrollment__user=user,
course__enrollment__mentor__isnull=False,
).values('pk')
Instead of exclude() you can create complex queries using Q(), or in your case ~Q():
filter_q = Q(course__enrollment__user=user) | ~Q(course__enrollment__mentor=None)
CourseType.objects.filter(filter_q).values('pk')
This might lead to a different SQL statement.
See docs:
https://docs.djangoproject.com/en/3.2/topics/db/queries/#complex-lookups-with-q-objects

SQL Joining the correct username

Say I have the following tables
User
__________
id
username
email
FriendGame
__________
id
useroneid
usertwoid
status
I want to get games that the current user is part of, so I do this:
SELECT *
FROM FriendGame
WHERE useroneid=1663702020516206
OR usertwoid=1663702020516206
AND STATUS =1;
This is fine. Now I want to join the username, but only for the user that ISNT the supplied user (1663702020516206) since in FriendGame the given user exists EITHER as useroneid or usertwoid.
You can pretty much translate your logic directly into an on clause:
SELECT fg.*
FROM FriendGame fg JOIN
User u
ON (fg.useroneid = 1663702020516206 and fg.usertwoid = u.id) or
(fg.usertwoid = 1663702020516206 and fg.useroneid = u.id)
WHERE 1663702020516206 in (fg.useroneid, fg.usertwoid) AND
STATUS = 1;
Actually, the where clause is not necessary to get the right result set, but I think it makes the intention of the query clearer.

List specific with a single table

I have following messages table:
I want to list all messages in a inbox like for example,
lets assume I am user number 1 so my inbox should show
me following:
Messages with user 2
Messages with user 3
Messages with user 4
What is the best way to build a query for this?
I've came up with somewhat weird query that looks like: (pseudo code)
SELECT
*
FROM
`message`
WHERE
(`user_id_sender` = 1 AND `user_id_receiver` NOT 1)
OR
(`user_id_sender` NOT 1 AND `user_id_receiver` = 1)
Thank you
That's very simple
SELECT user_id_sender ID,
(SELECT user_id_reciever
FROM messages
WHERE user_id_sender NOT IN(ID)
GROUP BY user_id_reciever
)
FROM message

Select name of creator and editor from user table

How I can select name of creator and editor from users table, creator and editor are different ids in same table, but user table is different table
Is it what you mean?
$sql = "
SELECT id,name
FROM users
WHERE users.id = editors_table.$editor_id
OR users.id = creators_table.$creator_id";
from what i understand, are you saying you have 3 tables - 1 with creator data, 1 with editor data, and a third that references a record in each of the tables using an id?
if so, you'll have to use JOINs to achieve what you want - something like:
SELECT id, name, editors_table.editor_id, creators_table.creator_id
FROM users
LEFT JOIN editors_table ON user.editor_id = editor_table.editor_id
LEFT JOIN creators_table ON user.creator_id = creator_table.creator_id
WHERE editor_table.editor_id = $editor_id_var
OR creator_table.creator_id = $creator_id_var
(you'll want to go through the query as I'm guessing here)