๐Ÿ›์žฅ๊ณ  ๋ฐฑ์˜คํ”ผ์Šค ์ˆ˜์ •-3

ํŒ”๋ฆฌ๋™ยท2021๋…„ 5์›” 11์ผ

๐Ÿ›์žฅ๊ณ  ๋ฐฑ์˜คํ”ผ์Šค ์ˆ˜์ •-3

Logentry

from django.contrib.admin.models import LogEntry, CHANGE   >>>> 2.
from django.contrib.contenttypes.models import ContentType >>>>> 2.

def refund(modeladmin, request, queryset):
    with transaction.atomic():
        qs = queryset.filter(~Q(status='ํ™˜๋ถˆ'))
        ct = ContentType.objects.get_for_model(queryset.model)
        for obj in qs:
            obj.product.stock += obj.quantity
            obj.product.save()

            LogEntry.objects.log_action(   >>>>> 3.
                user_id = request.user.id,
                content_type_id=ct.pk,
                object_id=obj.pk,
                object_repr='์ฃผ๋ฌธ ํ™˜๋ถˆ',
                action_flag=CHANGE,
                change_message='์ฃผ๋ฌธ ํ™˜๋ถˆ'
            )
        qs.update(status='ํ™˜๋ถˆ')
  1. ์žฅ๊ณ  ์–ด๋“œ๋ฏผ ํŽ˜์ด์ง€์— ๋ชจ๋ธ์„ ๋ณ€๊ฒฝํ•˜๋ฉด ๊ธฐ๋ก์ด ๋‚จ๋Š”๋ฐ ์ด ๋ถ€๋ถ„์„ ์ปค์Šคํ„ฐ๋งˆ์ด์ง•์„ ํ–ˆ๋‹ค.

  2. ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด์„œ ์•„๋ž˜ ๊ฒƒ๋“ค์„ ์ž„ํฌํŠธ ํ–ˆ๋‹ค.

    • from django.contrib.admin.models import LogEntry, CHANGE
    • from django.contrib.contenttypes.models import ContentType
  3. ๊ธฐ์กด์— ๊ตฌํ˜„ํ•ด ๋†“์€ ๋ฐ˜๋ณต๋ฌธ์— Logentry๊ฐ์ฒด์— ์ ‘๊ทผํ•ด์„œ ๊ฐ’๋“ค์„ ๋ฐ”๊พธ์–ด์ค€๋‹ค.

Logentry ํด๋ž˜์Šค

class LogEntry(models.Model):
    action_time = models.DateTimeField(
        _('action time'),
        default=timezone.now,
        editable=False,
    )
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        models.CASCADE,
        verbose_name=_('user'),
    )
    content_type = models.ForeignKey(
        ContentType,
        models.SET_NULL,
        verbose_name=_('content type'),
        blank=True, null=True,
    )
    object_id = models.TextField(_('object id'), blank=True, null=True)
    # Translators: 'repr' means representation (https://docs.python.org/library/functions.html#repr)
    object_repr = models.CharField(_('object repr'), max_length=200)
    action_flag = models.PositiveSmallIntegerField(_('action flag'), choices=ACTION_FLAG_CHOICES)
    # change_message is either a string or a JSON structure
    change_message = models.TextField(_('change message'), blank=True)

    objects = LogEntryManager()  >>>>>>>>>>>>>>>>>>>>>>>>>
  • Logentryํด๋ž˜์Šค๋ฅผ ๊นŒ๋ณด๋ฉด objects๋ณ€์ˆ˜์— LogEntryManager() ํด๋ž˜์Šค๊ฐ€ ๋‹ด๊ฒจ์žˆ๋‹ค.
  • LogEntryManager()ํด๋ž˜์Šค๋ฅผ ์‚ดํŽด๋ณด์ž.

LogEntryManagerํด๋ž˜์Šค

class LogEntryManager(models.Manager):
    use_in_migrations = True

    def log_action(self, user_id, content_type_id, object_id, object_repr, action_flag, change_message=''):
        if isinstance(change_message, list):
            change_message = json.dumps(change_message)
        return self.model.objects.create(
            user_id=user_id,
            content_type_id=content_type_id,
            object_id=str(object_id),
            object_repr=object_repr[:200],
            action_flag=action_flag,
            change_message=change_message,
        )
  • log_action ๋งค์†Œ๋“œ๊ฐ€ ์žˆ๋‹ค. ์ด ๋ถ€๋ถ„์„ ๋ฐ”๊ฟ”์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.
  • 2๋ฒˆ์งธ ์ธ์ž์ธ(self ์ œ์™ธ) content_type_id ๊ฐ’์—๋Š” content_type_id๊ฐ€ ํ•„์š”ํ•œ๋ฐ....
from django.contrib.admin.models import LogEntry, CHANGE   >>>> 2.
from django.contrib.contenttypes.models import ContentType >>>>> 2.

def refund(modeladmin, request, queryset):
    with transaction.atomic():
        qs = queryset.filter(~Q(status='ํ™˜๋ถˆ'))
        ct = ContentType.objects.get_for_model(queryset.model) >>>> 4. 
        for obj in qs:
            obj.product.stock += obj.quantity
            obj.product.save()

            LogEntry.objects.log_action(   >>>>> 3.
                user_id = request.user.id,
                content_type_id=ct.pk,
                object_id=obj.pk,
                object_repr='์ฃผ๋ฌธ ํ™˜๋ถˆ',
                action_flag=CHANGE,
                change_message='์ฃผ๋ฌธ ํ™˜๋ถˆ'
            )
        qs.update(status='ํ™˜๋ถˆ')
  1. ContentType ๊ฐ์ฒด์— ์ ‘๊ทผํ•ด์„œ ๋ชจ๋ธ์„ ๋ฝ‘์•„์˜จ๋‹ค.
  • ContentType ๋ฅผ ๊นŒ๋ณด๋ฉด objects ๋ณ€์ˆ˜์— ContentTypeManager()ํด๋ž˜์Šค๊ฐ€ ๋‹ด๊ฒจ์žˆ๊ณ  ์ด๊ฑธ ๊นŒ๋ณด๋ฉด

get_for_model ๋ฉ”์†Œ๋“œ

def get_for_model(self, model, for_concrete_model=True):
        """
        Return the ContentType object for a given model, creating the
        ContentType if necessary. Lookups are cached so that subsequent lookups
        for the same model don't hit the database.
        """
        opts = self._get_opts(model, for_concrete_model)
        try:
            return self._get_from_cache(opts)
        except KeyError:
            pass

        # The ContentType entry was not found in the cache, therefore we
        # proceed to load or create it.
        try:
            # Start with get() and not get_or_create() in order to use
            # the db_for_read (see #20401).
            ct = self.get(app_label=opts.app_label, model=opts.model_name)
        except self.model.DoesNotExist:
            # Not found in the database; we proceed to create it. This time
            # use get_or_create to take care of any race conditions.
            ct, created = self.get_or_create(
                app_label=opts.app_label,
                model=opts.model_name,
            )
        self._add_to_cache(self.db, ct)
        return ct
  • ์ˆ˜๋งŽ์€ ๋ฉ”์†Œ๋“œ ์ค‘์— get_for_model ์ด๋ž€ ๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ๋‹ค.

  • ์ด ํ•จ์ˆ˜์— ์ธ์ž๋กœ ์ฟผ๋ฆฌ์…‹์—์„œ ๋ฐ˜์€ ๋ชจ๋ธ์„ ์ธ์ž๋กœ ๋„˜๊ฒจ์„œ ct์— ์ €์žฅํ•œ๋‹ค.

  • Logentry์„ค์ •์„ ๋งˆ๋ฌด๋ฆฌํ•ด์ค€๋‹ค.

changelist์— ํ™˜๋ถˆ ๋ฒ„ํŠผ ์ถ”๊ฐ€

ํ™˜๋ถˆ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ƒํƒœ๊ฐ€ ๋ณ€ํ•˜๋Š” ๊ฒƒ์„ ๋งŒ๋“ค์–ด๋ณด์ž.


class OrderAdmin(admin.ModelAdmin):
    list_filter = ('status',)
    list_display = ('fcuser', 'product', 'styled_status', 'action')
    change_list_template = 'admin/order_change_list.html' >>>1
    actions = [
        refund
    ]
    def action(self, obj):  >>>> 2.
        if obj.status != 'ํ™˜๋ถˆ':
            return format_html(f'<input type="button" value="ํ™˜๋ถˆ"token interpolation">{obj.id})" class="btn btn-primary btn-sm">')
  • ์ €์ €๋ฒˆ ํฌ์ŠคํŠธ์—์„œ ํ–ˆ๋˜ ๊ฒฐ์ œ ์™„๋ฃŒ ๋‚˜ ํ™˜๋ถˆ ๊ธ€์”จ ์ƒ‰๊น”์„ ๋ฐ”๊พธ๋Š” ๋กœ์ง์œผ๋กœ ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค.
  1. change_list_template ์„ ์ƒˆ๋กœ๋งŒ๋“  htmlํŽ˜์ด์ง€๋กœ ์—ฐ๊ฒฐํ•ด์ฃผ์—ˆ๋‹ค.

  2. actionํ•จ์ˆ˜๋ฅผ ๋ณด๋ฉด obj๋ฅผ ์ธ์ž๋กœ ๋ฐ›๋Š”๋ฐ print๋ฌธ์œผ๋กœ ์ฐ์–ด๋ณด๋ฉด Order๋ชจ๋ธ์ด ๋‚˜์˜จ๋‹ค.

    • ๋งŒ์•ฝ ์ƒํƒœ๊ฐ€ ํ™˜๋ถˆ์ด ์•„๋‹ˆ๋ฉด ํ™˜๋ถˆ๋ฒ„ํŠผ์„ ์ƒ์„ฑํ•ด์ค€๋‹ค. onclick์˜ต์…˜์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค๊ณ  ์ธ์ž๋กœ obj์˜ id๊ฐ’์„ ๋„ฃ์–ด์ค€๋‹ค.

change_list_template

{% extends 'admin/change_list.html' %}
{% load static %}
{% block extrahead %}
{{ block.super }}
<script>
    function order_refund_submit(obj_id) {
        jQuery("#changelist-form").append('<input type="hiden" name="obj_id" value="' + obj_id + '">');
        jQuery("#changelist-form").submit();
    }
</script>
{% endblock %}  
  • extends 'admin/ change_list.html' -> ์žฅ๊ณ ์•ˆ์— ์žˆ๋Š” ์–ด๋“œ๋ฏผ ํŽ˜์ด์ง€ ๋ฆฌ์ŠคํŠธ html๋ฌธ์„œ๋ฅผ ์ต์Šคํ…๋“œํ–ˆ๋‹ค.
  • ๋ธ”๋Ÿญ์•ˆ์— ์ œ์ด์ฟผ๋ฆฌ ์ฝ”๋“œ๋ฅผ ๋„ฃ์—ˆ๋‹ค. ์ œ์ด์ฟผ๋ฆฌ๋ฅผ ์ž˜ ๋ชฐ๋ผ์„œ ์œ ์ถ”ํ•ด๋ณด์ž๋ฉด
    • ์•„๊นŒ actionํ•จ์ˆ˜์—์„œ onclick์ด๋ฒคํŠธ์— ํ•จ์ˆ˜์— ์ธ์ž๋กœ obj.id๋ฅผ ๋„˜๊ฒจ์ฃผ์—ˆ๊ณ 
    • changelistform์— inputํƒ€์ž…์ด hiden์ด๊ณ  value์— obj_id๋ฅผ ๋„ฃ์–ด์ฃผ๊ณ  submitํ•œ๋‹ค.

changelist_view

def changelist_view(self, request, extra_context=None):
        ## ์›ํ•˜๋Š” ๋™์ž‘
        extra_context = { 'title': '์ฃผ๋ฌธ ๋ชฉ๋ก' }
# ์ถ”๊ฐ€๋œ ์ฝ”๋“œ 
        if request.method == 'POST':
            obj_id = request.POST.get('obj_id')
            if obj_id:
                qs = Order.objects.filter(pk=obj_id)
                ct = ContentType.objects.get_for_model(qs.model)
                for obj in qs:
                    obj.product.stock += obj.quantity
                    obj.product.save()
                    LogEntry.objects.log_action(
                        user_id = request.user.id,
                        content_type_id=ct.pk,
                        object_id=obj.pk,
                        object_repr='์ฃผ๋ฌธ ํ™˜๋ถˆ',
                        action_flag=CHANGE,
                        change_message='์ฃผ๋ฌธ ํ™˜๋ถˆ'
                    )
                qs.update(status='ํ™˜๋ถˆ')
  • jQuery์—์„œ ๋„˜๊ฒจ์ค€ ๊ฐ’์„ ๋ฐ›๋Š”๋‹ค.
  • changelist_view๋„ ์žฅ๊ณ ์—์„œ ๋ทฐ๋‹จ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋กœ์ง๊ณผ ๋น„์Šทํ•˜๋‹ค.
  • POST๋กœ ๋ฐ›์œผ๋ฉด ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋ฉฐ
    • request.POST.get('obj_id')๊ฐ€ ์žˆ์œผ๋ฉด ๋‹ค์Œ์œผ๋กœ ๋„˜์–ด๊ฐ„๋‹ค.
    • ๋‚˜๋จธ์ง€๋Š” refundํ•จ์ˆ˜์™€ ๋น„์Šทํ•˜๊ณ  qs์— Order๋ชจ๋ธ์— pk๊ฐ’์ด obj_id(request.Post.get)์ธ ๊ฐ’์„ ์ €์žฅํ•œ๋‹ค.
profile
๋ฐฐ์›€์˜ ๊ธฐ๋ก

0๊ฐœ์˜ ๋Œ“๊ธ€