2024-10-27 18:22:01 +00:00
import email
from django . contrib import admin
from django . shortcuts import render , redirect
from leaflet . admin import LeafletGeoAdmin
from leaflet . admin import LeafletGeoAdminMixin
from leaflet_admin_list . admin import LeafletAdminListMixin
2025-08-30 02:20:25 +09:00
from . models import RogUser , Location2025 , SystemSettings , JoinedEvent , Favorite , TravelList , TravelPoint , ShapeLayers , Event , Location_line , Location_polygon , JpnAdminMainPerf , Useractions , CustomUser , GifuAreas , UserTracks , templocation , UserUpload , EventUser , GoalImages , CheckinImages , NewEvent2 , Team , NewCategory , Entry , Member , TempUser , GifurogeRegister , GpsLog , GpsCheckin , Checkpoint , Waypoint
2024-10-27 18:22:01 +00:00
from django . contrib . auth . admin import UserAdmin
from django . urls import path , reverse
from django . shortcuts import render
from django import forms ;
import requests
from django . http import HttpResponseRedirect
from django . utils . html import format_html
from . forms import CSVUploadForm
from . views import process_csv_upload
from django . db . models import F # F式をインポート
from django . db import transaction
from django . contrib import messages
import csv
from io import StringIO , TextIOWrapper
from datetime import timedelta
from django . contrib . auth . hashers import make_password
from datetime import datetime , date
from django . core . exceptions import ValidationError
from django . contrib . auth . forms import UserChangeForm , UserCreationForm
from django . utils . translation import gettext_lazy as _
2025-01-22 17:14:56 +09:00
from . services . csv_processor import EntryCSVProcessor
@admin.register ( Entry )
class EntryAdmin ( admin . ModelAdmin ) :
2025-01-23 17:24:42 +09:00
list_display = [ ' team ' , ' zekken_number ' , ' event ' , ' category ' , ' date ' , ' is_active ' ]
2025-01-22 17:14:56 +09:00
2025-01-22 19:39:16 +09:00
# change_list_templateの追加
change_list_template = ' admin/entry/change_list.html ' # この行を追加
2025-01-22 17:14:56 +09:00
def get_urls ( self ) :
from django . urls import path
urls = super ( ) . get_urls ( )
custom_urls = [
path ( ' upload-csv/ ' , self . upload_csv_view , name = ' entry_upload_csv ' ) ,
]
return custom_urls + urls
def upload_csv_view ( self , request ) :
processor = EntryCSVProcessor ( )
return processor . process_upload ( request )
2024-10-27 18:22:01 +00:00
@admin.register ( GifurogeRegister )
class GifurogeRegisterAdmin ( admin . ModelAdmin ) :
list_display = ( ' event_code ' , ' time ' , ' owner_name ' , ' email ' , ' team_name ' , ' department ' )
change_list_template = ' admin/rog/gifurogeregister/change_list.html ' # この行を追加
def find_matching_category ( self , time , department ) :
"""
時間とdepartmentに基づいて適切なカテゴリを見つける
"""
try :
duration = timedelta ( hours = time )
# 検索前の情報出力
print ( f " Searching for category with parameters: " )
print ( f " - Duration: { duration } " )
print ( f " - Department: { department } " )
# 利用可能なカテゴリの一覧を出力
all_categories = NewCategory . objects . all ( )
print ( " Available categories: " )
for cat in all_categories :
#print(f" - ID: {cat.id}")
print ( f " - Name: { cat . category_name } " )
print ( f " - Duration: { cat . duration } " )
print ( f " - Number: { cat . category_number } " )
# カテゴリ検索のクエリをログ出力
query = NewCategory . objects . filter (
duration = duration ,
category_name__startswith = department
)
print ( f " Query SQL: { query . query } " )
# 検索結果の取得
category = query . first ( )
if category :
print ( f " Found matching category: " )
print ( f " - Name: { category . category_name } " )
print ( f " - Duration: { category . duration } " )
print ( f " - Category Number: { getattr ( category , ' category_number ' , ' N/A ' ) } " )
else :
print ( " No matching category found with the following filters: " )
print ( f " - Duration equals: { duration } " )
print ( f " - Category name starts with: { department } " )
return category
except Exception as e :
print ( f " Error finding category: { e } " )
print ( f " Exception type: { type ( e ) } " )
import traceback
print ( f " Traceback: { traceback . format_exc ( ) } " )
return None
def create_entry_with_number ( self , team , category , owner , event ) :
"""
カテゴリ番号をインクリメントしてエントリーを作成
"""
try :
with transaction . atomic ( ) :
# 事前バリデーション
try :
# チームメンバーの性別をチェック
if category . female :
for member in team . members . all ( ) :
print ( f " Check existing member { member . user . lastname } { member . user . firstname } female: { member . user . female } " )
if not member . user . female :
raise ValidationError ( f " チーム ' { team . team_name } ' に男性メンバーが含まれているため、 "
f " カテゴリー ' { category . category_name } ' には参加できません。 " )
except ValidationError as e :
print ( f " Pre-validation error: { str ( e ) } " )
raise
# カテゴリを再度ロックして取得
category_for_update = NewCategory . objects . select_for_update ( ) . get (
category_name = category . category_name
)
print ( f " Creating entry with following details: " )
print ( f " - Category: { category_for_update . category_name } " )
print ( f " - Current category number: { category_for_update . category_number } " )
# イベントの日付を取得
entry_date = event . start_datetime . date ( )
# 既存のエントリーをチェック
existing_entry = Entry . objects . filter (
team = team ,
event = event ,
date = entry_date ,
is_active = True # アクティブなエントリーのみをチェック
) . first ( )
if existing_entry :
print ( f " Found existing entry for team { team . team_name } on { entry_date } " )
raise ValidationError (
f " Team { team . team_name } already has an entry for event { event . event_name } on { entry_date } "
)
# 現在の番号を取得してインクリメント
current_number = category_for_update . category_number
zekken_number = current_number
# カテゴリ番号をインクリメント
category_for_update . category_number = F ( ' category_number ' ) + 1
category_for_update . save ( )
# 変更後の値を取得して表示
category_for_update . refresh_from_db ( )
print ( f " Updated category number: { category_for_update . category_number } " )
# エントリーの作成
try :
entry = Entry . objects . create (
date = event . start_datetime ,
team = team ,
category = category ,
owner = owner ,
event = event ,
zekken_number = zekken_number ,
is_active = True
)
# バリデーションを実行
entry . full_clean ( )
# 問題なければ保存
entry . save ( )
print ( f " Created entry: " )
print ( f " - Team: { team . team_name } " )
print ( f " - Event: { event . event_name } " )
print ( f " - Category: { category . category_name } " )
print ( f " - Zekken Number: { zekken_number } " )
return entry
except ValidationError as e :
print ( f " Entry validation error: { str ( e ) } " )
raise
except Exception as e :
print ( f " Error creating entry: { e } " )
print ( f " Exception type: { type ( e ) } " )
import traceback
print ( f " Traceback: { traceback . format_exc ( ) } " )
raise
def split_full_name ( self , full_name ) :
"""
フルネームを姓と名に分割
半角または全角スペースに対応
"""
try :
# 空白文字で分割(半角スペース、全角スペース、タブなど)
parts = full_name . replace ( ' ' , ' ' ) . split ( )
if len ( parts ) > = 2 :
last_name = parts [ 0 ]
first_name = ' ' . join ( parts [ 1 : ] ) # 名が複数単語の場合に対応
return last_name , first_name
else :
# 分割できない場合は全体を姓とする
return full_name , ' '
except Exception as e :
print ( f " Error splitting name ' { full_name } ' : { e } " )
return full_name , ' '
def convert_japanese_date ( self , date_text ) :
"""
日本式の日付テキストをDateField形式に変換
例 : ' 1990年1月1日 ' - > datetime . date ( 1990 , 1 , 1 )
"""
try :
if not date_text or date_text . strip ( ) == ' ' :
return None
# 全角数字を半角数字に変換
date_text = date_text . translate ( str . maketrans ( ' 0 1 2 3 4 5 6 7 8 9 ' , ' 0123456789 ' ) )
date_text = date_text . strip ( )
# 区切り文字の判定と分割
if ' 年 ' in date_text :
# 年月日形式の場合
date_parts = date_text . replace ( ' 年 ' , ' - ' ) . replace ( ' 月 ' , ' - ' ) . replace ( ' 日 ' , ' ' ) . split ( ' - ' )
elif ' / ' in date_text :
# スラッシュ区切りの場合
date_parts = date_text . split ( ' / ' )
elif ' - ' in date_text :
date_parts = date_text . split ( ' - ' )
else :
print ( f " Unsupported date format: { date_text } " )
return None
# 部分の数を確認
if len ( date_parts ) != 3 :
print ( f " Invalid date parts count: { len ( date_parts ) } in ' { date_text } ' " )
return None
year = int ( date_parts [ 0 ] )
month = int ( date_parts [ 1 ] )
day = int ( date_parts [ 2 ] )
# 簡単な妥当性チェック
if not ( 1900 < = year < = 2100 ) :
print ( f " Invalid year: { year } " )
return None
if not ( 1 < = month < = 12 ) :
print ( f " Invalid month: { month } " )
return None
if not ( 1 < = day < = 31 ) : # 月ごとの日数チェックは省略
print ( f " Invalid day: { day } " )
return None
print ( f " Converted from { date_text } to year- { year } / month- { month } / day- { day } " )
return date ( year , month , day )
except Exception as e :
print ( f " Error converting date ' { date_text } ' : { str ( e ) } " )
return None
def create_owner_member ( self , team , row ) :
"""
オーナーをチームメンバー1として作成
既存のメンバーは更新
"""
try :
owner_name = row . get ( ' owner_name ' ) . strip ( )
# 姓名を分割
last_name , first_name = self . split_full_name ( owner_name )
print ( f " Split name - Last: { last_name } , First: { first_name } " )
# 誕生日の処理
birthday = row . get ( f ' owner_birthday ' , ' ' ) . strip ( )
birth_date = self . convert_japanese_date ( birthday )
print ( f " Converted birthday: { birth_date } " )
# 性別の処理
sex = row . get ( f ' owner_sex ' , ' ' ) . strip ( )
is_female = sex in [ ' 女性 ' , ' 女 ' , ' 女子 ' , ' female ' , ' girl ' , ' lady ' ]
print ( f " Sex: { sex } , is_female: { is_female } " )
# メンバーを作成
member , created = Member . objects . get_or_create (
team = team ,
user = team . owner ,
defaults = {
' is_temporary ' : True # 仮登録
}
)
# 既存メンバーの場合は情報を更新
if not created :
member . lastname = last_name
member . firstname = first_name
member . date_of_birth = birth_date
member . female = is_female
member . is_temporary = True
member . save ( )
print ( f " Updated existing member { last_name } { first_name } " )
else :
print ( f " Created new member { last_name } { first_name } " )
return member
except Exception as e :
print ( f " Error creating/updating member: { e } " )
raise
def create_members ( self , team , row ) :
"""
チームのメンバーを作成
既存のメンバーは更新
"""
try :
created_members = [ ]
# オーナーをメンバーに登録
member = self . create_owner_member ( team , row )
created_members . append ( member )
# メンバー2から5までを処理
for i in range ( 2 , 6 ) :
member_name = row . get ( f ' member { i } ' , ' ' ) . strip ( )
if member_name :
print ( f " ===== Processing member: { member_name } ===== " )
# 姓名を分割
last_name , first_name = self . split_full_name ( member_name )
print ( f " Split name - Last: { last_name } , First: { first_name } " )
# 誕生日の処理
birthday = row . get ( f ' birthday { i } ' , ' ' ) . strip ( )
birth_date = self . convert_japanese_date ( birthday )
print ( f " Converted birthday: { birth_date } " )
# 性別の処理
sex = row . get ( f ' sex { i } ' , ' ' ) . strip ( )
is_female = sex in [ ' 女性 ' , ' 女 ' , ' 女子 ' , ' female ' , ' girl ' , ' lady ' ]
print ( f " Sex: { sex } , is_female: { is_female } " )
# メンバー用のユーザーを作成
email = f " dummy_ { team . id } _ { i } @gifuai.net " . lower ( )
member_user , created = CustomUser . objects . get_or_create (
email = email ,
defaults = {
' password ' : make_password ( ' temporary_password ' ) ,
' lastname ' : last_name ,
' firstname ' : first_name ,
' date_of_birth ' : birth_date ,
' female ' : is_female
}
)
# 既存ユーザーの場合も姓名を更新
if not created :
member_user . lastname = last_name
member_user . firstname = first_name
member_user . date_of_birth = birth_date
member_user . female = is_female
member_user . save ( )
try :
# メンバーを作成
member , created = Member . objects . get_or_create (
team = team ,
user = member_user ,
defaults = {
' is_temporary ' : True # 仮登録
}
)
# 既存メンバーの場合は情報を更新
if not created :
member . is_temporary = True
member . save ( )
print ( f " Updated existing member { member_user . lastname } { member_user . firstname } " )
else :
print ( f " Created new member { member_user . lastname } { member_user . firstname } " )
created_members . append ( member )
print ( f " - Birthday: { member_user . date_of_birth } " )
print ( f " - Sex: { ' Female ' if member_user . female else ' Male ' } " )
except Exception as e :
print ( f " Error creating/updating member: { e } " )
raise
return created_members
except Exception as e :
print ( f " Error creating members: { e } " )
print ( f " Exception type: { type ( e ) } " )
import traceback
print ( f " Traceback: { traceback . format_exc ( ) } " )
raise
def get_urls ( self ) :
urls = super ( ) . get_urls ( )
custom_urls = [
path ( ' upload-csv/ ' , self . upload_csv , name = ' gifuroge_register_upload_csv ' ) ,
]
return custom_urls + urls
def upload_csv ( self , request ) :
print ( " upload_csv " )
if request . method == ' POST ' :
print ( " POST " )
if ' csv_file ' not in request . FILES :
messages . error ( request , ' No file was uploaded. ' )
return redirect ( ' .. ' )
csv_file = request . FILES [ ' csv_file ' ]
print ( f " csv_file(1) = { csv_file } " )
if not csv_file . name . endswith ( ' .csv ' ) :
messages . error ( request , ' File is not CSV type ' )
return redirect ( ' .. ' )
try :
# BOMを考慮してファイルを読み込む
file_content = csv_file . read ( )
# BOMがある場合は除去
if file_content . startswith ( b ' \xef \xbb \xbf ' ) :
file_content = file_content [ 3 : ]
# デコード
file_content = file_content . decode ( ' utf-8 ' )
csv_file = StringIO ( file_content )
reader = csv . DictReader ( csv_file )
print ( f " csv_file(2) = { csv_file } " )
print ( f " reader = { reader } " )
with transaction . atomic ( ) :
for row in reader :
print ( f " row= { row } " )
# オーナーの姓名を分割
owner_lastname , owner_firstname = self . split_full_name ( row [ ' owner_name ' ] )
# パスワードをハッシュ化
hashed_password = make_password ( row [ ' password ' ] )
# オーナーの誕生日の処理
owner_birthday = row . get ( ' owner_birthday ' , ' ' ) . strip ( )
owner_birth_date = self . convert_japanese_date ( owner_birthday )
print ( f " Owner birthday: { owner_birth_date } " )
# オーナーの性別の処理
owner_sex = row . get ( ' owner_sex ' , ' ' ) . strip ( )
owner_is_female = owner_sex in [ ' 女性 ' , ' 女 ' , ' 女子 ' , ' female ' , ' girl ' , ' lady ' ]
print ( f " Owner sex: { owner_sex } , is_female: { owner_is_female } " )
# ユーザーの取得または作成
user , created = CustomUser . objects . get_or_create (
email = row [ ' email ' ] ,
defaults = {
' password ' : hashed_password , # make_password(row['password'])
' lastname ' : owner_lastname ,
' firstname ' : owner_firstname ,
' date_of_birth ' : owner_birth_date ,
' female ' : owner_is_female
}
)
if not created :
# 既存ユーザーの場合、空のフィールドがあれば更新
should_update = False
update_fields = [ ]
print ( f " Checking existing user data for { user . email } : " )
print ( f " - Current lastname: ' { user . lastname } ' " )
print ( f " - Current firstname: ' { user . firstname } ' " )
print ( f " - Current birth date: { user . date_of_birth } " )
print ( f " - Current female: { user . female } " )
# 姓が空またはNoneの場合
if not user . lastname or user . lastname . strip ( ) == ' ' :
user . lastname = owner_lastname
should_update = True
update_fields . append ( ' lastname ' )
print ( f " - Updating lastname to: { owner_lastname } " )
# 名が空またはNoneの場合
if not user . firstname or user . firstname . strip ( ) == ' ' :
user . firstname = owner_firstname
should_update = True
update_fields . append ( ' firstname ' )
print ( f " - Updating firstname to: { owner_firstname } " )
# 生年月日が空またはNoneの場合
if not user . date_of_birth and owner_birth_date :
user . date_of_birth = owner_birth_date
should_update = True
update_fields . append ( ' date_of_birth ' )
print ( f " - Updating birth date to: { owner_birth_date } " )
# 性別が空またはNoneの場合
# Booleanフィールドなのでis None で判定
if user . female is None :
user . female = owner_is_female
should_update = True
update_fields . append ( ' female ' )
print ( f " - Updating female to: { owner_is_female } " )
# パスワードが'登録済み'でない場合のみ更新
if row [ ' password ' ] != ' 登録済み ' :
user . password = hashed_password
should_update = True
update_fields . append ( ' password ' )
print ( f " - Updating password " )
# 変更があった場合のみ保存
if should_update :
try :
# 特定のフィールドのみを更新
user . save ( update_fields = update_fields )
print ( f " Updated user { user . email } fields: { ' , ' . join ( update_fields ) } " )
except Exception as e :
print ( f " Error updating user { user . email } : { str ( e ) } " )
raise
else :
print ( f " No updates needed for user { user . email } " )
print ( f " user created... " )
print ( f " Owner member created: { user . lastname } { user . firstname } " )
print ( f " - Birthday: { user . date_of_birth } " )
print ( f " - Sex: { ' Female ' if user . female else ' Male ' } " )
# 適切なカテゴリを見つける
category = self . find_matching_category (
time = int ( row [ ' time ' ] ) ,
department = row [ ' department ' ]
)
if not category :
raise ValueError (
f " No matching category found for time= { row [ ' time ' ] } minutes "
f " and department= { row [ ' department ' ] } "
)
print ( f " Using category: { category . category_name } " )
# Teamの作成( 既存のチームがある場合は取得)
team , created = Team . objects . get_or_create (
team_name = row [ ' team_name ' ] ,
defaults = {
' owner ' : user ,
' category ' : category
}
)
# 既存のチームの場合でもカテゴリを更新
if not created :
team . category = category
team . save ( )
print ( " team created/updated... " )
self . create_members ( team , row )
# イベントの検索
try :
event_code = row [ ' event_code ' ]
event = NewEvent2 . objects . get ( event_name = event_code )
print ( f " Found event: { event . event_name } " )
except NewEvent2 . DoesNotExist :
raise ValueError ( f " Event with code { event_code } does not exist " )
try :
# エントリーの作成
entry = self . create_entry_with_number (
team = team ,
category = category ,
owner = user ,
event = event ,
)
print ( " entry created... " )
except ValidationError as e :
messages . error ( request , str ( e ) )
return redirect ( ' .. ' )
gifuroge_register = GifurogeRegister . objects . create (
event_code = row [ ' event_code ' ] ,
time = int ( row [ ' time ' ] ) ,
owner_name_kana = row [ ' owner_name_kana ' ] ,
owner_name = row [ ' owner_name ' ] ,
owner_birthday = self . convert_japanese_date ( row [ ' owner_birthday ' ] ) ,
owner_sex = row [ ' owner_sex ' ] ,
email = row [ ' email ' ] ,
password = row [ ' password ' ] ,
team_name = row [ ' team_name ' ] ,
department = row [ ' department ' ] ,
members_count = int ( row [ ' members_count ' ] ) ,
member2 = row . get ( ' member2 ' , ' ' ) or None ,
birthday2 = self . convert_japanese_date ( row . get ( ' birthday2 ' , ' ' ) ) ,
sex2 = row . get ( ' sex2 ' , ' ' ) or None ,
member3 = row . get ( ' member3 ' , ' ' ) or None ,
birthday3 = self . convert_japanese_date ( row . get ( ' birthday3 ' , ' ' ) ) ,
sex3 = row . get ( ' sex3 ' , ' ' ) or None ,
member4 = row . get ( ' member4 ' , ' ' ) or None ,
birthday4 = self . convert_japanese_date ( row . get ( ' birthday4 ' , ' ' ) ) ,
sex4 = row . get ( ' sex4 ' , ' ' ) or None ,
member5 = row . get ( ' member5 ' , ' ' ) or None ,
birthday5 = self . convert_japanese_date ( row . get ( ' birthday5 ' , ' ' ) ) ,
sex5 = row . get ( ' sex5 ' , ' ' ) or None
)
print ( f " saved gifuroge_register... " )
except UnicodeDecodeError :
messages . error ( request , ' File encoding error. Please ensure the file is UTF-8 encoded. ' )
return redirect ( ' .. ' )
except Exception as e :
print ( f " Error processing row: { e } " )
raise
messages . success ( request , ' CSV file uploaded successfully ' )
return redirect ( ' .. ' )
return render ( request , ' admin/rog/gifurogeregister/upload-csv.html ' )
class RogAdmin ( LeafletAdminListMixin , LeafletGeoAdminMixin , admin . ModelAdmin ) :
list_display = [ ' title ' , ' venue ' , ' at_date ' , ]
class ShopAdmin ( LeafletAdminListMixin , LeafletGeoAdminMixin , admin . ModelAdmin ) :
list_display = [ ' name ' , ]
class EventRouteAdmin ( LeafletAdminListMixin , LeafletGeoAdminMixin , admin . ModelAdmin ) :
list_display = [ ' name ' , ]
class ShopRouteAdmin ( LeafletAdminListMixin , LeafletGeoAdminMixin , admin . ModelAdmin ) :
list_display = [ ' name ' , ]
class loadUserForm ( forms . Form ) :
server_url = forms . CharField ( label = " Load Data from * " , initial = ' https://natnats.mobilous.com/get_team_list ' , widget = forms . Textarea ( attrs = { " rows " : 2 , " cols " : 95 } ) )
class UserAdminConfig ( UserAdmin ) :
search_fields = ( ' email ' , ' group ' , ' zekken_number ' , ' event_code ' , ' team_name ' , ' is_rogaining ' )
list_filter = ( ' email ' , ' group ' , ' is_rogaining ' )
ordering = ( ' email ' , )
list_display = ( ' email ' , ' group ' , ' zekken_number ' , ' event_code ' , ' team_name ' , ' is_active ' , ' is_staff ' , ' is_rogaining ' )
def get_urls ( self ) :
urls = super ( ) . get_urls ( )
new_url = [ path ( ' load-users/ ' , self . loadUsers ) , ]
return new_url + urls
def loadUsers ( self , request ) :
if request . method == " POST " :
frm = loadUserForm ( request . POST )
if frm . is_valid ( ) :
print ( frm . cleaned_data [ ' server_url ' ] )
#load json from server
url = frm . cleaned_data [ ' server_url ' ]
response = requests . get ( url )
data = response . json ( )
print ( " -------Event code-------- " )
print ( data )
print ( " -------Event code-------- " )
for i in data :
_exist = CustomUser . objects . filter ( email = i [ " zekken_number " ] ) . delete ( )
other_fields . setDefaut ( ' zekken_number ' , i [ ' zekken_number ' ] )
other_fields . setdefault ( ' is_staff ' , True )
other_fields . setdefault ( ' is_superuser ' , False )
other_fields . setdefault ( ' is_active ' , True )
other_fields . setdefault ( ' event_code ' , i [ ' event_code ' ] )
other_fields . setdefault ( ' team_name ' , i [ ' team_name ' ] )
other_fields . setdefault ( ' group ' , ' 大垣-初心者 ' )
usr = CustomUser . objects . create_user (
email = i [ " zekken_number " ] ,
password = i [ ' password ' ] ,
* * other_fields
)
form = loadUserForm ( )
data = { ' form ' : form }
return render ( request , ' admin/load_users.html ' , data )
"""
fieldsets = (
( None , { ' fields ' : ( ' email ' , ' group ' , ' zekken_number ' , ' event_code ' , ' team_name ' , ) } ) ,
( ' Permissions ' , { ' fields ' : ( ' is_staff ' , ' is_active ' , ' is_rogaining ' ) } ) ,
)
add_fieldsets = (
( None , { ' classes ' : ( ' wide ' , ) , ' fields ' : ( ' email ' , ' group ' , ' zekken_number ' , ' event_code ' , ' team_name ' , ' password1 ' , ' password2 ' ) } ) ,
)
"""
# readonly_fieldsを明示的に設定
readonly_fields = ( ' date_joined ' , ) # 変更不可のフィールドのみを指定=>Personal Infoも編集可能にする。
fieldsets = (
( None , { ' fields ' : ( ' email ' , ' password ' ) } ) ,
( _ ( ' Personal info ' ) , {
' fields ' : ( ' firstname ' , ' lastname ' , ' date_of_birth ' , ' female ' ) ,
' classes ' : ( ' wide ' , ) # フィールドの表示を広げる
} ) ,
( _ ( ' Permissions ' ) , { ' fields ' : ( ' is_staff ' , ' is_active ' , ' is_rogaining ' , ' user_permissions ' ) } ) ,
( _ ( ' Rogaining info ' ) , {
' fields ' : ( ' zekken_number ' , ' event_code ' , ' team_name ' , ' group ' ) ,
' classes ' : ( ' wide ' , )
} ) ,
( _ ( ' Important dates ' ) , {
' fields ' : ( ' date_joined ' , ' last_login ' ) ,
' classes ' : ( ' wide ' , )
} ) , # 読み取り専用
)
add_fieldsets = (
( None , {
' classes ' : ( ' wide ' , ) ,
#'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active', 'is_rogaining')}
' fields ' : ( ' email ' , ' password1 ' , ' password2 ' , ' lastname ' , ' firstname ' , ' date_of_birth ' , ' female ' , ' is_staff ' , ' is_active ' , ' is_rogaining ' ) }
) ,
)
search_fields = ( ' email ' , ' firstname ' , ' lastname ' , ' zekken_number ' , ' team_name ' )
ordering = ( ' email ' , )
class JpnSubPerfAdmin ( LeafletGeoAdmin ) :
search_fields = ( ' adm0_ja ' , ' adm1_ja ' , ' adm2_ja ' , ' name_modified ' , ' area_name ' , )
list_filter = ( ' adm0_ja ' , ' adm0_ja ' , ' name_modified ' , )
ordering = ( ' adm0_ja ' , )
list_display = ( ' adm0_ja ' , ' adm1_ja ' , ' adm2_ja ' , ' name_modified ' , ' area_name ' , )
class LocationAdmin ( LeafletGeoAdmin ) :
search_fields = ( ' location_id ' , ' cp ' , ' location_name ' , ' category ' , ' event_name ' , ' group ' , )
list_filter = ( ' event_name ' , ' group ' , )
ordering = ( ' location_id ' , ' cp ' , )
2025-08-31 10:01:42 +09:00
list_display = ( ' location_id ' , ' sub_loc_id ' , ' cp ' , ' location_name ' , ' photos ' , ' category ' , ' group ' , ' event_name ' , ' auto_checkin ' , ' checkin_radius ' , ' checkin_point ' , ' buy_point ' , )
2024-10-27 18:22:01 +00:00
def tranfer_to_location ( modeladmin , request , queryset ) :
tmp_locs = templocation . objects . all ( ) ;
for l in tmp_locs :
2025-08-30 02:20:25 +09:00
found = Location2025 . objects . filter ( location_id = l . location_id ) . exists ( )
2024-10-27 18:22:01 +00:00
if found :
2025-08-30 02:20:25 +09:00
Location2025 . objects . filter ( location_id = l . location_id ) . update (
2024-10-27 18:22:01 +00:00
sub_loc_id = l . sub_loc_id ,
cp = l . cp ,
location_name = l . location_name ,
category = l . category ,
subcategory = l . subcategory ,
zip = l . zip ,
address = l . address ,
prefecture = l . prefecture ,
area = l . area ,
city = l . city ,
latitude = l . latitude ,
longitude = l . longitude ,
photos = l . photos ,
videos = l . videos ,
webcontents = l . webcontents ,
status = l . status ,
portal = l . portal ,
group = l . group ,
phone = l . phone ,
fax = l . fax ,
email = l . email ,
facility = l . facility ,
remark = l . remark ,
tags = l . tags ,
hidden_location = l . hidden_location ,
auto_checkin = l . auto_checkin ,
checkin_radius = l . checkin_radius ,
checkin_point = l . checkin_point ,
buy_point = l . buy_point ,
evaluation_value = l . evaluation_value ,
shop_closed = l . shop_closed ,
shop_shutdown = l . shop_shutdown ,
opening_hours_mon = l . opening_hours_mon ,
opening_hours_tue = l . opening_hours_tue ,
opening_hours_wed = l . opening_hours_wed ,
opening_hours_thu = l . opening_hours_thu ,
opening_hours_fri = l . opening_hours_fri ,
opening_hours_sat = l . opening_hours_sat ,
opening_hours_sun = l . opening_hours_sun ,
geom = l . geom
)
else :
2025-08-30 02:20:25 +09:00
loc = Location2025 (
2024-10-27 18:22:01 +00:00
location_id = l . location_id ,
sub_loc_id = l . sub_loc_id ,
cp = l . cp ,
location_name = l . location_name ,
category = l . category ,
subcategory = l . subcategory ,
zip = l . zip ,
address = l . address ,
prefecture = l . prefecture ,
area = l . area ,
city = l . city ,
latitude = l . latitude ,
longitude = l . longitude ,
photos = l . photos ,
videos = l . videos ,
webcontents = l . webcontents ,
status = l . status ,
portal = l . portal ,
group = l . group ,
phone = l . phone ,
fax = l . fax ,
email = l . email ,
facility = l . facility ,
remark = l . remark ,
tags = l . tags ,
hidden_location = l . hidden_location ,
auto_checkin = l . auto_checkin ,
checkin_radius = l . checkin_radius ,
checkin_point = l . checkin_point ,
buy_point = l . buy_point ,
evaluation_value = l . evaluation_value ,
shop_closed = l . shop_closed ,
shop_shutdown = l . shop_shutdown ,
opening_hours_mon = l . opening_hours_mon ,
opening_hours_tue = l . opening_hours_tue ,
opening_hours_wed = l . opening_hours_wed ,
opening_hours_thu = l . opening_hours_thu ,
opening_hours_fri = l . opening_hours_fri ,
opening_hours_sat = l . opening_hours_sat ,
opening_hours_sun = l . opening_hours_sun ,
geom = l . geom
)
loc . save ( )
l . delete ( )
2025-01-22 18:29:54 +09:00
return True
2025-01-22 18:31:49 +09:00
#tranfer_to_location.short_description = "Transfer all locations in temp table to location table"
2024-10-27 18:22:01 +00:00
class TempLocationAdmin ( LeafletGeoAdmin ) :
search_fields = ( ' location_id ' , ' cp ' , ' location_name ' , ' category ' , ' event_name ' , )
list_filter = ( ' category ' , ' event_name ' , )
ordering = ( ' location_id ' , ' cp ' , )
2025-08-31 10:01:42 +09:00
list_display = ( ' location_id ' , ' cp ' , ' location_name ' , ' category ' , ' event_name ' , ' auto_checkin ' , ' checkin_radius ' , ' checkin_point ' , ' buy_point ' , )
2024-10-27 18:22:01 +00:00
actions = [ tranfer_to_location , ]
@admin.register ( NewEvent2 )
class NewEvent2Admin ( admin . ModelAdmin ) :
list_display = [ ' event_name ' , ' start_datetime ' , ' end_datetime ' , ' csv_upload_button ' ]
def get_urls ( self ) :
urls = super ( ) . get_urls ( )
my_urls = [
path ( ' csv-upload/ ' , self . admin_site . admin_view ( self . csv_upload_view ) , name = ' newevent2_csv_upload ' ) ,
]
return my_urls + urls
def csv_upload_view ( self , request ) :
if request . method == ' POST ' :
form = CSVUploadForm ( request . POST , request . FILES )
if form . is_valid ( ) :
csv_file = request . FILES [ ' csv_file ' ]
event = form . cleaned_data [ ' event ' ]
process_csv_upload ( csv_file , event )
self . message_user ( request , " CSV file has been processed successfully. " )
return HttpResponseRedirect ( " ../ " )
else :
form = CSVUploadForm ( )
return render ( request , ' admin/csv_upload.html ' , { ' form ' : form } )
def csv_upload_button ( self , obj ) :
url = reverse ( ' admin:newevent2_csv_upload ' )
return format_html ( ' <a class= " button " href= " {} " >CSVアップロード</a> ' , url )
csv_upload_button . short_description = ' CSV Upload '
def changelist_view ( self , request , extra_context = None ) :
extra_context = extra_context or { }
extra_context [ ' csv_upload_url ' ] = reverse ( ' admin:newevent2_csv_upload ' )
return super ( ) . changelist_view ( request , extra_context = extra_context )
@admin.register ( Team )
class TeamAdmin ( admin . ModelAdmin ) :
list_display = [ ' team_name ' , ' owner ' ]
search_fields = [ ' team_name ' , ' owner__email ' ]
@admin.register ( NewCategory )
class NewCategoryAdmin ( admin . ModelAdmin ) :
list_display = [ ' category_name ' , ' category_number ' , ' duration ' , ' num_of_member ' , ' family ' , ' female ' ]
list_filter = [ ' family ' , ' female ' ]
search_fields = [ ' category_name ' ]
2025-01-22 18:18:03 +09:00
#@admin.register(Entry)
#class EntryAdmin(admin.ModelAdmin):
# list_display = ['team', 'event', 'category', 'date']
# list_filter = ['event', 'category']
# search_fields = ['team__team_name', 'event__event_name']
2024-10-27 18:22:01 +00:00
@admin.register ( Member )
class MemberAdmin ( admin . ModelAdmin ) :
list_display = [ ' team ' , ' user ' ]
search_fields = [ ' team__team_name ' , ' user__email ' ]
@admin.register ( TempUser )
class TempUserAdmin ( admin . ModelAdmin ) :
list_display = [ ' email ' , ' is_rogaining ' , ' zekken_number ' , ' event_code ' , ' team_name ' , ' group ' , ' created_at ' , ' expires_at ' ]
list_filter = [ ' is_rogaining ' , ' group ' ]
search_fields = [ ' email ' , ' zekken_number ' , ' team_name ' ]
# CustomUserAdmin の修正(既存のものを更新)
class CustomUserChangeForm ( UserChangeForm ) :
class Meta ( UserChangeForm . Meta ) :
model = CustomUser
fields = ' __all__ '
class CustomUserCreationForm ( UserCreationForm ) :
class Meta ( UserCreationForm . Meta ) :
model = CustomUser
fields = ( ' email ' , ' lastname ' , ' firstname ' , ' date_of_birth ' , ' female ' )
2025-01-22 08:19:49 +00:00
@admin.register ( CustomUser )
2024-10-27 18:22:01 +00:00
class CustomUserAdmin ( UserAdmin ) :
form = CustomUserChangeForm
add_form = CustomUserCreationForm
2025-01-22 08:19:49 +00:00
#model = CustomUser
2024-10-27 18:22:01 +00:00
list_display = ( ' email ' , ' is_staff ' , ' is_active ' , ' is_rogaining ' , ' zekken_number ' , ' event_code ' , ' team_name ' , ' group ' , ' firstname ' , ' lastname ' )
2025-01-22 17:14:56 +09:00
search_fields = ( ' egit mail ' , ' firstname ' , ' lastname ' , ' zekken_number ' )
2024-10-27 18:22:01 +00:00
list_filter = ( ' is_staff ' , ' is_active ' , ' is_rogaining ' , ' group ' )
2024-12-19 12:57:57 +09:00
ordering = ( ' email ' , )
2024-10-27 18:22:01 +00:00
# readonly_fieldsを明示的に設定
readonly_fields = ( ' date_joined ' , ) # 変更不可のフィールドのみを指定=>Personal Infoも編集可能にする。
fieldsets = (
( None , { ' fields ' : ( ' email ' , ' password ' ) } ) ,
( _ ( ' Personal info ' ) , {
' fields ' : ( ' firstname ' , ' lastname ' , ' date_of_birth ' , ' female ' ) ,
' classes ' : ( ' wide ' , ) # フィールドの表示を広げる
} ) ,
( _ ( ' Permissions ' ) , { ' fields ' : ( ' is_staff ' , ' is_active ' , ' is_rogaining ' , ' user_permissions ' ) } ) ,
( _ ( ' Rogaining info ' ) , {
' fields ' : ( ' zekken_number ' , ' event_code ' , ' team_name ' , ' group ' ) ,
' classes ' : ( ' wide ' , )
} ) ,
( _ ( ' Important dates ' ) , {
' fields ' : ( ' date_joined ' , ' last_login ' ) ,
' classes ' : ( ' wide ' , )
} ) , # 読み取り専用
)
add_fieldsets = (
( None , {
' classes ' : ( ' wide ' , ) ,
#'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active', 'is_rogaining')}
' fields ' : ( ' email ' , ' password1 ' , ' password2 ' , ' lastname ' , ' firstname ' , ' date_of_birth ' , ' female ' , ' is_staff ' , ' is_active ' , ' is_rogaining ' ) }
) ,
)
search_fields = ( ' email ' , ' firstname ' , ' lastname ' , ' zekken_number ' , ' team_name ' )
ordering = ( ' email ' , )
2025-01-22 08:19:49 +00:00
def get_readonly_fields_old ( self , request , obj = None ) :
2024-10-27 18:22:01 +00:00
# スーパーユーザーの場合は読み取り専用フィールドを最小限に
if request . user . is_superuser :
return self . readonly_fields
# 通常のスタッフユーザーの場合は追加の制限を設定可能
return self . readonly_fields + ( ' is_staff ' , ' is_superuser ' )
2025-01-22 08:19:49 +00:00
def get_readonly_fields ( self , request , obj = None ) :
if request . user . is_superuser :
return ( ' date_joined ' , ' last_login ' )
return ( ' date_joined ' , ' last_login ' , ' is_staff ' , ' is_superuser ' )
2025-01-22 18:36:04 +09:00
admin . site . register ( Useractions )
admin . site . register ( RogUser , admin . ModelAdmin )
admin . site . register ( SystemSettings , admin . ModelAdmin )
admin . site . register ( JoinedEvent , admin . ModelAdmin )
admin . site . register ( Favorite , admin . ModelAdmin )
admin . site . register ( TravelList , admin . ModelAdmin )
admin . site . register ( TravelPoint , admin . ModelAdmin )
admin . site . register ( Event , admin . ModelAdmin )
admin . site . register ( Location_line , LeafletGeoAdmin )
admin . site . register ( Location_polygon , LeafletGeoAdmin )
admin . site . register ( JpnAdminMainPerf , LeafletGeoAdmin )
admin . site . register ( UserTracks , LeafletGeoAdmin ) ;
#admin.site.register(JpnAdminPerf, LeafletGeoAdmin)
admin . site . register ( GifuAreas , LeafletGeoAdmin )
admin . site . register ( ShapeLayers , admin . ModelAdmin )
admin . site . register ( UserUpload , admin . ModelAdmin )
admin . site . register ( EventUser , admin . ModelAdmin )
#admin.site.register(UserUploadUser, admin.ModelAdmin)
#admin.site.register(ShapeFileLocations, admin.ModelAdmin)
2025-01-22 19:02:38 +09:00
#admin.site.register(CustomUser, UserAdminConfig)
2025-08-30 03:48:07 +09:00
# 古いtemplocationは無効化 - Location2025を使用
#admin.site.register(templocation, TempLocationAdmin)
2025-01-22 18:36:04 +09:00
admin . site . register ( GoalImages , admin . ModelAdmin )
admin . site . register ( CheckinImages , admin . ModelAdmin )
2025-08-24 19:44:36 +09:00
# GpsLogとその他の新しいモデルの登録
@admin.register ( GpsLog )
class GpsLogAdmin ( admin . ModelAdmin ) :
list_display = [ ' id ' , ' serial_number ' , ' zekken_number ' , ' event_code ' , ' cp_number ' , ' checkin_time ' ]
list_filter = [ ' event_code ' , ' checkin_time ' , ' buy_flag ' , ' is_service_checked ' ]
search_fields = [ ' zekken_number ' , ' event_code ' , ' cp_number ' ]
2025-08-29 18:39:20 +09:00
readonly_fields = [ ' checkin_time ' ]
2025-08-24 19:44:36 +09:00
@admin.register ( GpsCheckin )
class GpsCheckinAdmin ( admin . ModelAdmin ) :
2025-08-29 18:39:20 +09:00
list_display = [ ' id ' , ' zekken ' , ' event_code ' , ' cp_number ' , ' checkin_time ' ]
list_filter = [ ' event_code ' , ' checkin_time ' ]
2025-08-29 16:38:01 +09:00
search_fields = [ ' zekken ' , ' event_code ' , ' cp_number ' ]
2025-08-29 18:39:20 +09:00
readonly_fields = [ ' checkin_time ' , ' record_time ' ]
2025-08-24 19:44:36 +09:00
@admin.register ( Checkpoint )
class CheckpointAdmin ( admin . ModelAdmin ) :
list_display = [ ' id ' , ' cp_name ' , ' cp_number ' , ' photo_point ' , ' buy_point ' ]
search_fields = [ ' cp_name ' , ' cp_number ' ]
list_filter = [ ' photo_point ' , ' buy_point ' ]
readonly_fields = [ ' created_at ' , ' updated_at ' ]
@admin.register ( Waypoint )
class WaypointAdmin ( admin . ModelAdmin ) :
list_display = [ ' id ' , ' entry ' , ' latitude ' , ' longitude ' , ' recorded_at ' ]
search_fields = [ ' entry__team_name ' ]
list_filter = [ ' recorded_at ' ]
readonly_fields = [ ' created_at ' ]
@admin.register ( Location2025 )
class Location2025Admin ( LeafletGeoAdmin ) :
2025-08-30 04:12:55 +09:00
""" Location2025の管理画面( 全フィールド対応) """
2025-08-24 19:44:36 +09:00
list_display = [
2025-08-30 04:12:55 +09:00
' cp_number ' , ' cp_name ' , ' event ' , ' sub_loc_id ' , ' subcategory ' ,
' total_point ' , ' has_photos ' , ' has_videos ' , ' is_active ' ,
2025-08-24 19:44:36 +09:00
' csv_upload_date ' , ' created_at '
]
list_filter = [
' event ' , ' is_active ' , ' shop_closed ' , ' shop_shutdown ' ,
2025-08-30 04:12:55 +09:00
' subcategory ' , ' hidden_location ' ,
2025-08-24 19:44:36 +09:00
' csv_upload_date ' , ' created_at '
]
2025-08-30 04:12:55 +09:00
search_fields = [
' cp_name ' , ' address ' , ' description ' , ' remark ' , ' tags ' ,
' sub_loc_id ' , ' subcategory ' , ' evaluation_value '
]
2025-08-24 19:44:36 +09:00
readonly_fields = [
' csv_source_file ' , ' csv_upload_date ' , ' csv_upload_user ' ,
' created_at ' , ' updated_at ' , ' created_by ' , ' updated_by '
]
fieldsets = (
( ' 基本情報 ' , {
2025-08-30 04:12:55 +09:00
' fields ' : ( ' cp_number ' , ' event ' , ' cp_name ' , ' sub_loc_id ' , ' subcategory ' , ' is_active ' , ' sort_order ' )
2025-08-24 19:44:36 +09:00
} ) ,
( ' 位置情報 ' , {
' fields ' : ( ' latitude ' , ' longitude ' , ' location ' , ' address ' )
} ) ,
( ' ポイント設定 ' , {
2025-08-30 04:39:45 +09:00
' fields ' : ( ' checkin_point ' , ' buy_point ' )
2025-08-24 19:44:36 +09:00
} ) ,
( ' チェックイン設定 ' , {
' fields ' : ( ' checkin_radius ' , ' auto_checkin ' )
} ) ,
( ' 営業情報 ' , {
' fields ' : ( ' shop_closed ' , ' shop_shutdown ' , ' opening_hours ' )
} ) ,
( ' 詳細情報 ' , {
2025-08-30 04:12:55 +09:00
' fields ' : ( ' phone ' , ' website ' , ' description ' , ' remark ' )
} ) ,
( ' メディア・タグ情報 ' , {
' fields ' : ( ' photos ' , ' videos ' , ' tags ' , ' evaluation_value ' ) ,
' classes ' : ( ' wide ' , )
} ) ,
( ' 高度設定 ' , {
' fields ' : ( ' hidden_location ' , ) ,
' classes ' : ( ' collapse ' , )
2025-08-24 19:44:36 +09:00
} ) ,
( ' CSV情報 ' , {
' fields ' : ( ' csv_source_file ' , ' csv_upload_date ' , ' csv_upload_user ' ) ,
' classes ' : ( ' collapse ' , )
} ) ,
( ' 管理情報 ' , {
' fields ' : ( ' created_at ' , ' updated_at ' , ' created_by ' , ' updated_by ' ) ,
' classes ' : ( ' collapse ' , )
} ) ,
)
2025-08-30 04:12:55 +09:00
def has_photos ( self , obj ) :
""" 写真データ有無の表示 """
return bool ( obj . photos and obj . photos . strip ( ) )
has_photos . boolean = True
has_photos . short_description = ' 写真 '
def has_videos ( self , obj ) :
""" 動画データ有無の表示 """
return bool ( obj . videos and obj . videos . strip ( ) )
has_videos . boolean = True
has_videos . short_description = ' 動画 '
2025-08-24 19:44:36 +09:00
# CSV一括アップロード機能
change_list_template = ' admin/location2025/change_list.html '
def get_urls ( self ) :
from django . urls import path
urls = super ( ) . get_urls ( )
custom_urls = [
path ( ' upload-csv/ ' , self . upload_csv_view , name = ' location2025_upload_csv ' ) ,
path ( ' export-csv/ ' , self . export_csv_view , name = ' location2025_export_csv ' ) ,
]
return custom_urls + urls
def upload_csv_view ( self , request ) :
""" CSVアップロード画面 """
if request . method == ' POST ' :
if ' csv_file ' in request . FILES and ' event ' in request . POST :
csv_file = request . FILES [ ' csv_file ' ]
event_id = request . POST [ ' event ' ]
try :
from . models import NewEvent2
event = NewEvent2 . objects . get ( id = event_id )
# CSVインポート実行
result = Location2025 . import_from_csv (
csv_file ,
event ,
user = request . user
)
# 結果メッセージ
if result [ ' errors ' ] :
messages . warning (
request ,
f " インポート完了: 作成 { result [ ' created ' ] } 件, 更新 { result [ ' updated ' ] } 件, "
f " エラー { len ( result [ ' errors ' ] ) } 件 - { ' ; ' . join ( result [ ' errors ' ] [ : 5 ] ) } "
)
else :
messages . success (
request ,
f " CSVインポートが完了しました。作成: { result [ ' created ' ] } 件, 更新: { result [ ' updated ' ] } 件 "
)
except Exception as e :
messages . error ( request , f " CSVインポートエラー: { str ( e ) } " )
return redirect ( ' .. ' )
2025-08-30 03:48:07 +09:00
# フォーム表示 - Location2025システム用
2025-08-24 19:44:36 +09:00
from . models import NewEvent2
2025-08-31 10:01:42 +09:00
# スタッフユーザーの場合は全ステータスのイベントを表示
if request . user . is_staff :
events = NewEvent2 . objects . all ( ) . order_by ( ' -start_datetime ' )
else :
events = NewEvent2 . objects . filter ( status = ' public ' ) . order_by ( ' -start_datetime ' )
2025-08-24 19:44:36 +09:00
return render ( request , ' admin/location2025/upload_csv.html ' , {
' events ' : events ,
' title ' : ' チェックポイントCSVアップロード '
} )
def export_csv_view ( self , request ) :
2025-08-30 04:39:45 +09:00
""" CSVエクスポート( 全フィールド対応) """
2025-08-24 19:44:36 +09:00
import csv
from django . http import HttpResponse
from django . utils import timezone
response = HttpResponse ( content_type = ' text/csv; charset=utf-8 ' )
2025-08-30 04:39:45 +09:00
response [ ' Content-Disposition ' ] = f ' attachment; filename= " checkpoints_enhanced_ { timezone . now ( ) . strftime ( " % Y % m %d _ % H % M % S " ) } .csv " '
2025-08-24 19:44:36 +09:00
# BOM付きUTF-8で出力
response . write ( ' \ufeff ' )
writer = csv . writer ( response )
2025-08-30 04:39:45 +09:00
# 全フィールドのヘッダー
2025-08-24 19:44:36 +09:00
writer . writerow ( [
2025-08-30 04:39:45 +09:00
' cp_number ' , ' cp_name ' , ' latitude ' , ' longitude ' , ' checkin_point ' ,
' buy_point ' , ' address ' , ' phone ' , ' description ' ,
' sub_loc_id ' , ' subcategory ' , ' photos ' , ' videos ' , ' tags ' ,
' evaluation_value ' , ' remark ' , ' hidden_location '
2025-08-24 19:44:36 +09:00
] )
queryset = self . get_queryset ( request )
for obj in queryset :
writer . writerow ( [
obj . cp_number , obj . cp_name , obj . latitude , obj . longitude ,
2025-08-30 04:39:45 +09:00
obj . checkin_point , obj . buy_point ,
obj . address or ' ' , obj . phone or ' ' , obj . description or ' ' ,
obj . sub_loc_id or ' ' , obj . subcategory or ' ' , obj . photos or ' ' ,
obj . videos or ' ' , obj . tags or ' ' , obj . evaluation_value or ' ' ,
obj . remark or ' ' , obj . hidden_location
2025-08-24 19:44:36 +09:00
] )
return response
def save_model ( self , request , obj , form , change ) :
""" 保存時にユーザー情報を自動設定 """
if not change : # 新規作成時
obj . created_by = request . user
obj . updated_by = request . user
super ( ) . save_model ( request , obj , form , change )