mirror of
https://github.com/seanmorley15/AdventureLog.git
synced 2025-07-19 12:59:36 +02:00
Refactor user ID handling to use UUIDs; update related components and serializers for consistency
This commit is contained in:
parent
129c76078e
commit
86d213bb8b
18 changed files with 78 additions and 46 deletions
|
@ -2,8 +2,10 @@ from django.utils import timezone
|
|||
import os
|
||||
from .models import Adventure, AdventureImage, ChecklistItem, Collection, Note, Transportation, Checklist, Visit, Category
|
||||
from rest_framework import serializers
|
||||
from main.utils import CustomModelSerializer
|
||||
|
||||
class AdventureImageSerializer(serializers.ModelSerializer):
|
||||
|
||||
class AdventureImageSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = AdventureImage
|
||||
fields = ['id', 'image', 'adventure']
|
||||
|
@ -19,11 +21,12 @@ class AdventureImageSerializer(serializers.ModelSerializer):
|
|||
representation['image'] = f"{public_url}/media/{instance.image.name}"
|
||||
return representation
|
||||
|
||||
class CategorySerializer(serializers.ModelSerializer):
|
||||
class CategorySerializer(CustomModelSerializer):
|
||||
num_adventures = serializers.SerializerMethodField()
|
||||
class Meta:
|
||||
model = Category
|
||||
fields = ['id', 'name', 'display_name', 'icon', 'user_id']
|
||||
read_only_fields = ['id', 'user_id']
|
||||
fields = ['id', 'name', 'display_name', 'icon', 'user_id', 'num_adventures']
|
||||
read_only_fields = ['id', 'user_id', 'num_adventures']
|
||||
|
||||
def validate_name(self, value):
|
||||
if Category.objects.filter(name=value).exists():
|
||||
|
@ -31,14 +34,17 @@ class CategorySerializer(serializers.ModelSerializer):
|
|||
|
||||
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:
|
||||
model = Visit
|
||||
fields = ['id', 'start_date', 'end_date', 'notes']
|
||||
read_only_fields = ['id']
|
||||
|
||||
class AdventureSerializer(serializers.ModelSerializer):
|
||||
class AdventureSerializer(CustomModelSerializer):
|
||||
images = AdventureImageSerializer(many=True, read_only=True)
|
||||
visits = VisitSerializer(many=True, read_only=False)
|
||||
category = CategorySerializer(read_only=True)
|
||||
|
@ -102,7 +108,7 @@ class AdventureSerializer(serializers.ModelSerializer):
|
|||
|
||||
return instance
|
||||
|
||||
class TransportationSerializer(serializers.ModelSerializer):
|
||||
class TransportationSerializer(CustomModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Transportation
|
||||
|
@ -113,7 +119,7 @@ class TransportationSerializer(serializers.ModelSerializer):
|
|||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
|
||||
|
||||
class NoteSerializer(serializers.ModelSerializer):
|
||||
class NoteSerializer(CustomModelSerializer):
|
||||
|
||||
class Meta:
|
||||
model = Note
|
||||
|
@ -123,7 +129,7 @@ class NoteSerializer(serializers.ModelSerializer):
|
|||
]
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'user_id']
|
||||
|
||||
class ChecklistItemSerializer(serializers.ModelSerializer):
|
||||
class ChecklistItemSerializer(CustomModelSerializer):
|
||||
class Meta:
|
||||
model = ChecklistItem
|
||||
fields = [
|
||||
|
@ -131,7 +137,7 @@ class ChecklistItemSerializer(serializers.ModelSerializer):
|
|||
]
|
||||
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')
|
||||
class Meta:
|
||||
model = Checklist
|
||||
|
@ -194,7 +200,7 @@ class ChecklistSerializer(serializers.ModelSerializer):
|
|||
|
||||
return data
|
||||
|
||||
class CollectionSerializer(serializers.ModelSerializer):
|
||||
class CollectionSerializer(CustomModelSerializer):
|
||||
adventures = AdventureSerializer(many=True, read_only=True, source='adventure_set')
|
||||
transportations = TransportationSerializer(many=True, read_only=True, source='transportation_set')
|
||||
notes = NoteSerializer(many=True, read_only=True, source='note_set')
|
||||
|
|
10
backend/server/main/utils.py
Normal file
10
backend/server/main/utils.py
Normal 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
|
|
@ -196,6 +196,7 @@ class CustomUserDetailsSerializer(UserDetailsSerializer):
|
|||
# remove any ' from the url
|
||||
public_url = public_url.replace("'", "")
|
||||
representation['profile_pic'] = f"{public_url}/media/{instance.profile_pic.name}"
|
||||
del representation['pk'] # remove the pk field from the response
|
||||
return representation
|
||||
|
||||
class MyPasswordResetSerializer(PasswordResetSerializer):
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import os
|
||||
from .models import Country, Region, VisitedRegion
|
||||
from rest_framework import serializers
|
||||
from main.utils import CustomModelSerializer
|
||||
|
||||
|
||||
class CountrySerializer(serializers.ModelSerializer):
|
||||
def get_public_url(self, obj):
|
||||
|
@ -36,7 +38,7 @@ class RegionSerializer(serializers.ModelSerializer):
|
|||
fields = '__all__'
|
||||
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)
|
||||
latitude = serializers.DecimalField(source='region.latitude', max_digits=9, decimal_places=6, read_only=True)
|
||||
name = serializers.CharField(source='region.name', read_only=True)
|
||||
|
|
|
@ -167,7 +167,7 @@
|
|||
<div class="card-actions justify-end mt-2">
|
||||
<!-- action options dropdown -->
|
||||
{#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 tabindex="0" role="button" class="btn btn-neutral-200">
|
||||
<DotsHorizontal class="w-6 h-6" />
|
||||
|
@ -188,7 +188,7 @@
|
|||
</button>
|
||||
|
||||
<!-- 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}
|
||||
><LinkVariantRemove class="w-6 h-6" />{$t(
|
||||
'adventures.remove_from_collection'
|
||||
|
|
|
@ -52,7 +52,14 @@
|
|||
location: null,
|
||||
images: [],
|
||||
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;
|
||||
|
@ -73,7 +80,14 @@
|
|||
user_id: adventureToEdit?.user_id || null,
|
||||
collection: adventureToEdit?.collection || collection?.id || null,
|
||||
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[] = [];
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
on:change={() => toggleSelect(type.name)}
|
||||
checked={types.indexOf(type.name) > -1}
|
||||
/>
|
||||
<span>{type.display_name + ' ' + type.icon}</span>
|
||||
<span>{type.display_name + ' ' + type.icon + ` (${type.num_adventures})`}</span>
|
||||
</label>
|
||||
</li>
|
||||
{/each}
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
<button class="btn btn-neutral-200 mb-2" on:click={editChecklist}>
|
||||
<Launch class="w-6 h-6" />{$t('notes.open')}
|
||||
</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
|
||||
id="delete_adventure"
|
||||
data-umami-event="Delete Checklist"
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
name: newItem,
|
||||
is_checked: newStatus,
|
||||
id: '',
|
||||
user_id: 0,
|
||||
user_id: '',
|
||||
checklist: 0,
|
||||
created_at: '',
|
||||
updated_at: ''
|
||||
|
@ -135,7 +135,7 @@
|
|||
<p class="font-semibold text-md mb-2">{$t('checklist.editing_checklist')} {initialName}</p>
|
||||
{/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>
|
||||
<div class="form-control mb-2">
|
||||
<label for="name">{$t('adventures.name')}</label>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import Calendar from '~icons/mdi/calendar';
|
||||
|
||||
let newCollection: Collection = {
|
||||
user_id: NaN,
|
||||
user_id: '',
|
||||
id: '',
|
||||
name: '',
|
||||
description: '',
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<button class="btn btn-neutral-200 mb-2" on:click={editNote}>
|
||||
<Launch class="w-6 h-6" />{$t('notes.open')}
|
||||
</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
|
||||
id="delete_adventure"
|
||||
data-umami-event="Delete Adventure"
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
<p class="font-semibold text-md mb-2">{$t('notes.editing_note')} {initialName}</p>
|
||||
{/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>
|
||||
<div class="form-control mb-2">
|
||||
<label for="name">{$t('adventures.name')}</label>
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
{/if}
|
||||
</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">
|
||||
<button on:click={deleteTransportation} class="btn btn-secondary"
|
||||
><TrashCanOutline class="w-5 h-5 mr-1" /></button
|
||||
|
|
|
@ -13,7 +13,7 @@ export type User = {
|
|||
|
||||
export type Adventure = {
|
||||
id: string;
|
||||
user_id: number | null;
|
||||
user_id: string | null;
|
||||
type: string;
|
||||
name: string;
|
||||
location?: string | null;
|
||||
|
@ -43,7 +43,7 @@ export type Adventure = {
|
|||
name: string;
|
||||
display_name: string;
|
||||
icon: string;
|
||||
user_id: number;
|
||||
user_id: string;
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -69,7 +69,7 @@ export type Region = {
|
|||
export type VisitedRegion = {
|
||||
id: number;
|
||||
region: number;
|
||||
user_id: number;
|
||||
user_id: string;
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
name: string;
|
||||
|
@ -87,7 +87,7 @@ export type Point = {
|
|||
|
||||
export type Collection = {
|
||||
id: string;
|
||||
user_id: number;
|
||||
user_id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
is_public: boolean;
|
||||
|
@ -122,7 +122,7 @@ export type OpenStreetMapPlace = {
|
|||
|
||||
export type Transportation = {
|
||||
id: string;
|
||||
user_id: number;
|
||||
user_id: string;
|
||||
type: string;
|
||||
name: string;
|
||||
description: string | null;
|
||||
|
@ -141,7 +141,7 @@ export type Transportation = {
|
|||
|
||||
export type Note = {
|
||||
id: string;
|
||||
user_id: number;
|
||||
user_id: string;
|
||||
name: string;
|
||||
content: string | null;
|
||||
links: string[] | null;
|
||||
|
@ -154,7 +154,7 @@ export type Note = {
|
|||
|
||||
export type Checklist = {
|
||||
id: string;
|
||||
user_id: number;
|
||||
user_id: string;
|
||||
name: string;
|
||||
items: ChecklistItem[];
|
||||
date: string | null; // ISO 8601 date string
|
||||
|
@ -166,7 +166,7 @@ export type Checklist = {
|
|||
|
||||
export type ChecklistItem = {
|
||||
id: string;
|
||||
user_id: number;
|
||||
user_id: string;
|
||||
name: string;
|
||||
is_checked: boolean;
|
||||
checklist: number;
|
||||
|
@ -193,5 +193,6 @@ export type Category = {
|
|||
name: string;
|
||||
display_name: string;
|
||||
icon: string;
|
||||
user_id: number;
|
||||
user_id: string;
|
||||
num_adventures: number;
|
||||
};
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
console.log(data);
|
||||
|
||||
let adventures: Adventure[] = data.props.adventures || [];
|
||||
let categories: Category[] = data.props.categories || [];
|
||||
|
||||
let currentSort = {
|
||||
order_by: '',
|
||||
|
@ -36,14 +35,13 @@
|
|||
let typeString: string = '';
|
||||
|
||||
$: {
|
||||
console.log(typeString);
|
||||
if (typeof window !== 'undefined' && typeString) {
|
||||
if (typeof window !== 'undefined') {
|
||||
let url = new URL(window.location.href);
|
||||
if (typeString) {
|
||||
url.searchParams.set('types', typeString);
|
||||
goto(url.toString(), { invalidateAll: true, replaceState: true });
|
||||
} else if (typeof window !== 'undefined' && !typeString) {
|
||||
let url = new URL(window.location.href);
|
||||
url.searchParams.set('types', 'all');
|
||||
} else {
|
||||
url.searchParams.delete('types');
|
||||
}
|
||||
goto(url.toString(), { invalidateAll: true, replaceState: true });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,7 +82,7 @@
|
|||
{/if}
|
||||
|
||||
{#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]">
|
||||
<button class="btn m-1 size-16 btn-primary" on:click={() => (isEditModalOpen = true)}
|
||||
><ClipboardList class="w-8 h-8" /></button
|
||||
|
|
|
@ -263,7 +263,7 @@
|
|||
</div>
|
||||
{/if}
|
||||
{#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="flex flex-row items-center justify-center gap-4">
|
||||
<div class="dropdown dropdown-top dropdown-end">
|
||||
|
@ -275,7 +275,7 @@
|
|||
tabindex="0"
|
||||
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>
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
|
|
|
@ -41,13 +41,13 @@
|
|||
publicAdventures = data.props.adventures;
|
||||
|
||||
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 {
|
||||
myAdventures = [];
|
||||
}
|
||||
|
||||
publicAdventures = publicAdventures.filter(
|
||||
(adventure) => adventure.user_id !== data.user?.pk
|
||||
(adventure) => adventure.user_id !== data.user?.uuid
|
||||
);
|
||||
|
||||
if (data.props.osmData) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue