1
0
Fork 0
mirror of https://github.com/seanmorley15/AdventureLog.git synced 2025-07-22 22:39:36 +02:00

Refactor user ID handling to use UUIDs; update related components and serializers for consistency

This commit is contained in:
Sean Morley 2024-11-17 16:34:46 -05:00
parent 129c76078e
commit 86d213bb8b
18 changed files with 78 additions and 46 deletions

View file

@ -2,8 +2,10 @@ from django.utils import timezone
import os import os
from .models import Adventure, AdventureImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit, Category from .models import Adventure, AdventureImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit, Category
from rest_framework import serializers from rest_framework import serializers
from main.utils import CustomModelSerializer
class AdventureImageSerializer(serializers.ModelSerializer):
class AdventureImageSerializer(CustomModelSerializer):
class Meta: class Meta:
model = AdventureImage model = AdventureImage
fields = ['id', 'image', 'adventure'] fields = ['id', 'image', 'adventure']
@ -19,11 +21,12 @@ class AdventureImageSerializer(serializers.ModelSerializer):
representation['image'] = f"{public_url}/media/{instance.image.name}" representation['image'] = f"{public_url}/media/{instance.image.name}"
return representation return representation
class CategorySerializer(serializers.ModelSerializer): class CategorySerializer(CustomModelSerializer):
num_adventures = serializers.SerializerMethodField()
class Meta: class Meta:
model = Category model = Category
fields = ['id', 'name', 'display_name', 'icon', 'user_id'] fields = ['id', 'name', 'display_name', 'icon', 'user_id', 'num_adventures']
read_only_fields = ['id', 'user_id'] read_only_fields = ['id', 'user_id', 'num_adventures']
def validate_name(self, value): def validate_name(self, value):
if Category.objects.filter(name=value).exists(): if Category.objects.filter(name=value).exists():
@ -31,14 +34,17 @@ class CategorySerializer(serializers.ModelSerializer):
return value return value
class VisitSerializer(serializers.ModelSerializer): def get_num_adventures(self, obj):
return Adventure.objects.filter(category=obj, user_id=obj.user_id).count()
class VisitSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Visit model = Visit
fields = ['id', 'start_date', 'end_date', 'notes'] fields = ['id', 'start_date', 'end_date', 'notes']
read_only_fields = ['id'] read_only_fields = ['id']
class AdventureSerializer(serializers.ModelSerializer): class AdventureSerializer(CustomModelSerializer):
images = AdventureImageSerializer(many=True, read_only=True) images = AdventureImageSerializer(many=True, read_only=True)
visits = VisitSerializer(many=True, read_only=False) visits = VisitSerializer(many=True, read_only=False)
category = CategorySerializer(read_only=True) category = CategorySerializer(read_only=True)
@ -102,7 +108,7 @@ class AdventureSerializer(serializers.ModelSerializer):
return instance return instance
class TransportationSerializer(serializers.ModelSerializer): class TransportationSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Transportation model = Transportation
@ -113,7 +119,7 @@ class TransportationSerializer(serializers.ModelSerializer):
] ]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
class NoteSerializer(serializers.ModelSerializer): class NoteSerializer(CustomModelSerializer):
class Meta: class Meta:
model = Note model = Note
@ -123,7 +129,7 @@ class NoteSerializer(serializers.ModelSerializer):
] ]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id'] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
class ChecklistItemSerializer(serializers.ModelSerializer): class ChecklistItemSerializer(CustomModelSerializer):
class Meta: class Meta:
model = ChecklistItem model = ChecklistItem
fields = [ fields = [
@ -131,7 +137,7 @@ class ChecklistItemSerializer(serializers.ModelSerializer):
] ]
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'checklist'] read_only_fields = ['id', 'created_at', 'updated_at', 'user_id', 'checklist']
class ChecklistSerializer(serializers.ModelSerializer): class ChecklistSerializer(CustomModelSerializer):
items = ChecklistItemSerializer(many=True, source='checklistitem_set') items = ChecklistItemSerializer(many=True, source='checklistitem_set')
class Meta: class Meta:
model = Checklist model = Checklist
@ -194,7 +200,7 @@ class ChecklistSerializer(serializers.ModelSerializer):
return data return data
class CollectionSerializer(serializers.ModelSerializer): class CollectionSerializer(CustomModelSerializer):
adventures = AdventureSerializer(many=True, read_only=True, source='adventure_set') adventures = AdventureSerializer(many=True, read_only=True, source='adventure_set')
transportations = TransportationSerializer(many=True, read_only=True, source='transportation_set') transportations = TransportationSerializer(many=True, read_only=True, source='transportation_set')
notes = NoteSerializer(many=True, read_only=True, source='note_set') notes = NoteSerializer(many=True, read_only=True, source='note_set')

View file

@ -0,0 +1,10 @@
from rest_framework import serializers
def get_user_uuid(user):
return str(user.uuid)
class CustomModelSerializer(serializers.ModelSerializer):
def to_representation(self, instance):
representation = super().to_representation(instance)
representation['user_id'] = get_user_uuid(instance.user_id)
return representation

View file

@ -196,6 +196,7 @@ class CustomUserDetailsSerializer(UserDetailsSerializer):
# remove any ' from the url # remove any ' from the url
public_url = public_url.replace("'", "") public_url = public_url.replace("'", "")
representation['profile_pic'] = f"{public_url}/media/{instance.profile_pic.name}" representation['profile_pic'] = f"{public_url}/media/{instance.profile_pic.name}"
del representation['pk'] # remove the pk field from the response
return representation return representation
class MyPasswordResetSerializer(PasswordResetSerializer): class MyPasswordResetSerializer(PasswordResetSerializer):

View file

@ -1,6 +1,8 @@
import os import os
from .models import Country, Region, VisitedRegion from .models import Country, Region, VisitedRegion
from rest_framework import serializers from rest_framework import serializers
from main.utils import CustomModelSerializer
class CountrySerializer(serializers.ModelSerializer): class CountrySerializer(serializers.ModelSerializer):
def get_public_url(self, obj): def get_public_url(self, obj):
@ -36,7 +38,7 @@ class RegionSerializer(serializers.ModelSerializer):
fields = '__all__' fields = '__all__'
read_only_fields = ['id', 'name', 'country', 'longitude', 'latitude'] read_only_fields = ['id', 'name', 'country', 'longitude', 'latitude']
class VisitedRegionSerializer(serializers.ModelSerializer): class VisitedRegionSerializer(CustomModelSerializer):
longitude = serializers.DecimalField(source='region.longitude', max_digits=9, decimal_places=6, read_only=True) longitude = serializers.DecimalField(source='region.longitude', max_digits=9, decimal_places=6, read_only=True)
latitude = serializers.DecimalField(source='region.latitude', max_digits=9, decimal_places=6, read_only=True) latitude = serializers.DecimalField(source='region.latitude', max_digits=9, decimal_places=6, read_only=True)
name = serializers.CharField(source='region.name', read_only=True) name = serializers.CharField(source='region.name', read_only=True)

View file

@ -167,7 +167,7 @@
<div class="card-actions justify-end mt-2"> <div class="card-actions justify-end mt-2">
<!-- action options dropdown --> <!-- action options dropdown -->
{#if type != 'link'} {#if type != 'link'}
{#if adventure.user_id == user?.pk || (collection && user && collection.shared_with.includes(user.uuid))} {#if adventure.user_id == user?.uuid || (collection && user && collection.shared_with.includes(user.uuid))}
<div class="dropdown dropdown-end"> <div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-neutral-200"> <div tabindex="0" role="button" class="btn btn-neutral-200">
<DotsHorizontal class="w-6 h-6" /> <DotsHorizontal class="w-6 h-6" />
@ -188,7 +188,7 @@
</button> </button>
<!-- remove from collection --> <!-- remove from collection -->
{#if adventure.collection && user?.pk == adventure.user_id} {#if adventure.collection && user?.uuid == adventure.user_id}
<button class="btn btn-neutral mb-2" on:click={removeFromCollection} <button class="btn btn-neutral mb-2" on:click={removeFromCollection}
><LinkVariantRemove class="w-6 h-6" />{$t( ><LinkVariantRemove class="w-6 h-6" />{$t(
'adventures.remove_from_collection' 'adventures.remove_from_collection'

View file

@ -52,7 +52,14 @@
location: null, location: null,
images: [], images: [],
user_id: null, user_id: null,
collection: collection?.id || null collection: collection?.id || null,
category: {
id: '',
name: '',
display_name: '',
icon: '',
user_id: ''
}
}; };
export let adventureToEdit: Adventure | null = null; export let adventureToEdit: Adventure | null = null;
@ -73,7 +80,14 @@
user_id: adventureToEdit?.user_id || null, user_id: adventureToEdit?.user_id || null,
collection: adventureToEdit?.collection || collection?.id || null, collection: adventureToEdit?.collection || collection?.id || null,
visits: adventureToEdit?.visits || [], visits: adventureToEdit?.visits || [],
is_visited: adventureToEdit?.is_visited || false is_visited: adventureToEdit?.is_visited || false,
category: adventureToEdit?.category || {
id: '',
name: '',
display_name: '',
icon: '',
user_id: ''
}
}; };
let markers: Point[] = []; let markers: Point[] = [];

View file

@ -53,7 +53,7 @@
on:change={() => toggleSelect(type.name)} on:change={() => toggleSelect(type.name)}
checked={types.indexOf(type.name) > -1} checked={types.indexOf(type.name) > -1}
/> />
<span>{type.display_name + ' ' + type.icon}</span> <span>{type.display_name + ' ' + type.icon + ` (${type.num_adventures})`}</span>
</label> </label>
</li> </li>
{/each} {/each}

View file

@ -56,7 +56,7 @@
<button class="btn btn-neutral-200 mb-2" on:click={editChecklist}> <button class="btn btn-neutral-200 mb-2" on:click={editChecklist}>
<Launch class="w-6 h-6" />{$t('notes.open')} <Launch class="w-6 h-6" />{$t('notes.open')}
</button> </button>
{#if checklist.user_id == user?.pk || (collection && user && collection.shared_with.includes(user.uuid))} {#if checklist.user_id == user?.uuid || (collection && user && collection.shared_with.includes(user.uuid))}
<button <button
id="delete_adventure" id="delete_adventure"
data-umami-event="Delete Checklist" data-umami-event="Delete Checklist"

View file

@ -34,7 +34,7 @@
name: newItem, name: newItem,
is_checked: newStatus, is_checked: newStatus,
id: '', id: '',
user_id: 0, user_id: '',
checklist: 0, checklist: 0,
created_at: '', created_at: '',
updated_at: '' updated_at: ''
@ -135,7 +135,7 @@
<p class="font-semibold text-md mb-2">{$t('checklist.editing_checklist')} {initialName}</p> <p class="font-semibold text-md mb-2">{$t('checklist.editing_checklist')} {initialName}</p>
{/if} {/if}
{#if (checklist && user?.pk == checklist?.user_id) || (user && collection && collection.shared_with.includes(user.uuid)) || !checklist} {#if (checklist && user?.uuid == checklist?.user_id) || (user && collection && collection.shared_with.includes(user.uuid)) || !checklist}
<form on:submit|preventDefault> <form on:submit|preventDefault>
<div class="form-control mb-2"> <div class="form-control mb-2">
<label for="name">{$t('adventures.name')}</label> <label for="name">{$t('adventures.name')}</label>

View file

@ -8,7 +8,7 @@
import Calendar from '~icons/mdi/calendar'; import Calendar from '~icons/mdi/calendar';
let newCollection: Collection = { let newCollection: Collection = {
user_id: NaN, user_id: '',
id: '', id: '',
name: '', name: '',
description: '', description: '',

View file

@ -59,7 +59,7 @@
<button class="btn btn-neutral-200 mb-2" on:click={editNote}> <button class="btn btn-neutral-200 mb-2" on:click={editNote}>
<Launch class="w-6 h-6" />{$t('notes.open')} <Launch class="w-6 h-6" />{$t('notes.open')}
</button> </button>
{#if note.user_id == user?.pk || (collection && user && collection.shared_with.includes(user.uuid))} {#if note.user_id == user?.uuid || (collection && user && collection.shared_with.includes(user.uuid))}
<button <button
id="delete_adventure" id="delete_adventure"
data-umami-event="Delete Adventure" data-umami-event="Delete Adventure"

View file

@ -113,7 +113,7 @@
<p class="font-semibold text-md mb-2">{$t('notes.editing_note')} {initialName}</p> <p class="font-semibold text-md mb-2">{$t('notes.editing_note')} {initialName}</p>
{/if} {/if}
{#if (note && user?.pk == note?.user_id) || (collection && user && collection.shared_with.includes(user.uuid)) || !note} {#if (note && user?.uuid == note?.user_id) || (collection && user && collection.shared_with.includes(user.uuid)) || !note}
<form on:submit|preventDefault> <form on:submit|preventDefault>
<div class="form-control mb-2"> <div class="form-control mb-2">
<label for="name">{$t('adventures.name')}</label> <label for="name">{$t('adventures.name')}</label>

View file

@ -59,7 +59,7 @@
{/if} {/if}
</div> </div>
{#if transportation.user_id == user?.pk || (collection && user && collection.shared_with.includes(user.uuid))} {#if transportation.user_id == user?.uuid || (collection && user && collection.shared_with.includes(user.uuid))}
<div class="card-actions justify-end"> <div class="card-actions justify-end">
<button on:click={deleteTransportation} class="btn btn-secondary" <button on:click={deleteTransportation} class="btn btn-secondary"
><TrashCanOutline class="w-5 h-5 mr-1" /></button ><TrashCanOutline class="w-5 h-5 mr-1" /></button

View file

@ -13,7 +13,7 @@ export type User = {
export type Adventure = { export type Adventure = {
id: string; id: string;
user_id: number | null; user_id: string | null;
type: string; type: string;
name: string; name: string;
location?: string | null; location?: string | null;
@ -43,7 +43,7 @@ export type Adventure = {
name: string; name: string;
display_name: string; display_name: string;
icon: string; icon: string;
user_id: number; user_id: string;
}; };
}; };
@ -69,7 +69,7 @@ export type Region = {
export type VisitedRegion = { export type VisitedRegion = {
id: number; id: number;
region: number; region: number;
user_id: number; user_id: string;
longitude: number; longitude: number;
latitude: number; latitude: number;
name: string; name: string;
@ -87,7 +87,7 @@ export type Point = {
export type Collection = { export type Collection = {
id: string; id: string;
user_id: number; user_id: string;
name: string; name: string;
description: string; description: string;
is_public: boolean; is_public: boolean;
@ -122,7 +122,7 @@ export type OpenStreetMapPlace = {
export type Transportation = { export type Transportation = {
id: string; id: string;
user_id: number; user_id: string;
type: string; type: string;
name: string; name: string;
description: string | null; description: string | null;
@ -141,7 +141,7 @@ export type Transportation = {
export type Note = { export type Note = {
id: string; id: string;
user_id: number; user_id: string;
name: string; name: string;
content: string | null; content: string | null;
links: string[] | null; links: string[] | null;
@ -154,7 +154,7 @@ export type Note = {
export type Checklist = { export type Checklist = {
id: string; id: string;
user_id: number; user_id: string;
name: string; name: string;
items: ChecklistItem[]; items: ChecklistItem[];
date: string | null; // ISO 8601 date string date: string | null; // ISO 8601 date string
@ -166,7 +166,7 @@ export type Checklist = {
export type ChecklistItem = { export type ChecklistItem = {
id: string; id: string;
user_id: number; user_id: string;
name: string; name: string;
is_checked: boolean; is_checked: boolean;
checklist: number; checklist: number;
@ -193,5 +193,6 @@ export type Category = {
name: string; name: string;
display_name: string; display_name: string;
icon: string; icon: string;
user_id: number; user_id: string;
num_adventures: number;
}; };

View file

@ -15,7 +15,6 @@
console.log(data); console.log(data);
let adventures: Adventure[] = data.props.adventures || []; let adventures: Adventure[] = data.props.adventures || [];
let categories: Category[] = data.props.categories || [];
let currentSort = { let currentSort = {
order_by: '', order_by: '',
@ -36,14 +35,13 @@
let typeString: string = ''; let typeString: string = '';
$: { $: {
console.log(typeString); if (typeof window !== 'undefined') {
if (typeof window !== 'undefined' && typeString) {
let url = new URL(window.location.href); let url = new URL(window.location.href);
if (typeString) {
url.searchParams.set('types', typeString); url.searchParams.set('types', typeString);
goto(url.toString(), { invalidateAll: true, replaceState: true }); } else {
} else if (typeof window !== 'undefined' && !typeString) { url.searchParams.delete('types');
let url = new URL(window.location.href); }
url.searchParams.set('types', 'all');
goto(url.toString(), { invalidateAll: true, replaceState: true }); goto(url.toString(), { invalidateAll: true, replaceState: true });
} }
} }

View file

@ -82,7 +82,7 @@
{/if} {/if}
{#if adventure} {#if adventure}
{#if data.user && data.user.pk == adventure.user_id} {#if data.user && data.user.uuid == adventure.user_id}
<div class="fixed bottom-4 right-4 z-[999]"> <div class="fixed bottom-4 right-4 z-[999]">
<button class="btn m-1 size-16 btn-primary" on:click={() => (isEditModalOpen = true)} <button class="btn m-1 size-16 btn-primary" on:click={() => (isEditModalOpen = true)}
><ClipboardList class="w-8 h-8" /></button ><ClipboardList class="w-8 h-8" /></button

View file

@ -263,7 +263,7 @@
</div> </div>
{/if} {/if}
{#if collection} {#if collection}
{#if data.user && !collection.is_archived} {#if data.user && data.user.uuid && (data.user.uuid == collection.user_id || collection.shared_with.includes(data.user.uuid)) && !collection.is_archived}
<div class="fixed bottom-4 right-4 z-[999]"> <div class="fixed bottom-4 right-4 z-[999]">
<div class="flex flex-row items-center justify-center gap-4"> <div class="flex flex-row items-center justify-center gap-4">
<div class="dropdown dropdown-top dropdown-end"> <div class="dropdown dropdown-top dropdown-end">
@ -275,7 +275,7 @@
tabindex="0" tabindex="0"
class="dropdown-content z-[1] menu p-4 shadow bg-base-300 text-base-content rounded-box w-52 gap-4" class="dropdown-content z-[1] menu p-4 shadow bg-base-300 text-base-content rounded-box w-52 gap-4"
> >
{#if collection.user_id === data.user.pk} {#if collection.user_id === data.user.uuid}
<p class="text-center font-bold text-lg">{$t('adventures.link_new')}</p> <p class="text-center font-bold text-lg">{$t('adventures.link_new')}</p>
<button <button
class="btn btn-primary" class="btn btn-primary"

View file

@ -41,13 +41,13 @@
publicAdventures = data.props.adventures; publicAdventures = data.props.adventures;
if (data.user?.pk != null) { if (data.user?.pk != null) {
myAdventures = myAdventures.filter((adventure) => adventure.user_id === data.user?.pk); myAdventures = myAdventures.filter((adventure) => adventure.user_id === data.user?.uuid);
} else { } else {
myAdventures = []; myAdventures = [];
} }
publicAdventures = publicAdventures.filter( publicAdventures = publicAdventures.filter(
(adventure) => adventure.user_id !== data.user?.pk (adventure) => adventure.user_id !== data.user?.uuid
); );
if (data.props.osmData) { if (data.props.osmData) {