장고 동적url을 slug로 만들기
동적url을 int에서 slug로 변경
유저프로필 페이지의 url을 기존의 int형에서 slug형으로 변환하려고 한다.
유저의 닉네임을 slug로 사용할 것이다.
path(
'users/<int:user_id>/',
views.ProfileView.as_view(),
name='profile'
),
url에서 위의 코드를
path(
'users/<slug>/',
views.ProfileView.as_view(),
name='profile'
),
이렇게 slug를 사용하도록 바꾼다.
잘보면 원래의 형식이라면 <slug:slug>
이렇게 작성을 해야 하지만 slug에 한글이 들어갈 때 한글을 인식하지 못하는 문제가 생기게 된다.
slug 한글
위의 링크에서 제시한 방법대로 <slug>
로 작성을 하니 해결이 된다.
그리고 모델에 slug를 추가해야 한다.
from django.utils.text import slugify
class User(AbstractUser):
nickname = models.CharField(
max_length=15,
unique=True,
null=True,
error_messages={"unique": '해당 닉네임은 이미 사용중입니다!'},
validators = [validate_no_special_charactors],
)
slug = models.SlugField(
unique=True,
null=True,
)
def save(self, *args, **kwargs):
if self.nickname == None:
self.nickname = uuid.uuid1()
self.slug = slugify(self.nickname, allow_unicode=True)
super(User, self).save(*args, **kwargs)
def __str__(self):
return str(self.nickname)
-
slug 한글 에러
여기서도 slug에 한글이 들어갈 때 문제가 자꾸 생긴다.
그래서 해결법으로 allow_unicode=True를 보통은 slug=models.SlugField()에 작성을 하던데 내 경우에 전혀 해결이 되지 않았다.
allow_unicode
위 링크에서는 allow_unicode옵션을 save함수 안에서 slugify에 작성을 하였는데 이렇게하니 해결이 되었다. -
slug 자동 생성
nickname을 slug로 사용하려고 하는데 그러면 둘 다 똑같은 값이니 nickname이 생성될 때 slug가 자동으로 nickname과 똑같은 값으로 설정되게 하면 번거로움이 줄어들 것이다. 그러기 위해선 save함수를 오버라이드 하면 된다.
슬러그 자동 -
slug unique
slug를 url로 사용하려면 중복이 되면 안되니 반드시 unique옵션을 줘야한다.
이때 shell이나 어드민페이지에서 유저를 생성하면 문제가 생기게 된다. shell이나 어드민페이지에서 유저를 만들 때는 추가 필드는 입력을 받지 않는데 nickname은 추가 필드이기 때문에 입력을 받지 못하게 된다.
쿼리셋을 확인해 보면 {… ‘nickname’: None, ‘slug’: ‘none’} 이렇게 닉네임은 비어있고 슬러그는 ‘none’문자열이 들어가 있다.
이렇게 nickname이 비어있는 유저가 존재하는 채로 새로운 유저가 회원가입하면 django.db.utils.IntegrityError: UNIQUE constraint failed에러를 발생시키게 된다.
확실친 않지만 아마 모델이 생성될 때 기본 필드들이 먼저 생성되고 추가 필드들이 나중에 생성되는 과정에서 닉네임을 입력했더라도 일시적으로 슬러그가 ‘none’으로 되어있는 구간이 있는 것 같다.
그래서 닉네임이 None일 때는 닉네임을 uuid값으로 대체시키는 if문을 함께 작성했다. -
str 리턴
str함수의 리턴값을 str로 캐스팅 한것은 기존처럼 그냥 self.nickname으로 리턴을 하게되면 __str__ returned non-string (type NoneType)에러를 발생시키게 된다.
str리턴
그래서 str로 변환 후 리턴시킨다.
그리고 뷰에서
class ProfileView(DetailView):
model = User
template_name = 'user/profile.html'
pk_url_kwarg = 'user_id'
context_object_name = 'profile_user'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
user_id = self.kwargs.get('user_id')
context['user_posts'] = Post.objects.filter(author__id=user_id)[:5]
return context
이렇게 작성했던 것을
class ProfileView(DetailView):
model = User
template_name = 'user/profile.html'
slug_url_kwarg = 'slug'
context_object_name = 'profile_user'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
slug = self.kwargs.get('slug')
context['user_posts'] = Post.objects.filter(author__slug=slug)[:5]
return context
이렇게 바꾼다.
slug로 넘기게 url에서 설정했으니 받을 때도 에러가 나지않게 slug_url_kwarg로 받아와야 한다.
마찬가지로 템플릿에서 넘길 때도 slug를 사용하도록 수정해줘야 한다.
- url작성 순서
만약 이런식으로 여러 뷰들을 slug를 사용하도록 수정하고 에러가나서 확인을 해봤는데 잘못 작성한 부분이 없는 것 같으면 url의 순서를 다시 확인해보자.
장고는 작성한 url을 위에서 부터 읽어 내려오니 순서에 따라 에러가 날 수도 있다.
동적 url을 int형에서 slug형으로 바꿀 때 기존에는 잘 되었던 것이 이 문제로 인해 안될 수도 있다.
url의 위치를 바꾸던가 url경로를 수정하면 된다.path( 'users/<slug>/', views.ProfileView.as_view(), name='profile' ), path( 'users/<slug>/posts/', views.UserPostListView.as_view(), name='user-post-list' ), path( 'update-profile/', views.ProfileUpdateView.as_view(), name='profile-update' ),
profile경로가 profile-update경로보다 위에 있으니 계속 에러가 났다.
그래서 기존의'users/update-profile/'
에서 위와 같이 변경을 했다.
Leave a comment