Commit 7433327d authored by ibuler's avatar ibuler

[Feature] 标签管理功能

parent f37b3316
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
from django import forms from django import forms
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from .models import Cluster, Asset, AssetGroup, AdminUser, SystemUser from .models import Cluster, Asset, AssetGroup, AdminUser, SystemUser, Label
from common.utils import validate_ssh_private_key, ssh_pubkey_gen, ssh_key_gen, get_logger from common.utils import validate_ssh_private_key, ssh_pubkey_gen, ssh_key_gen, get_logger
...@@ -10,20 +10,20 @@ logger = get_logger(__file__) ...@@ -10,20 +10,20 @@ logger = get_logger(__file__)
class AssetCreateForm(forms.ModelForm): class AssetCreateForm(forms.ModelForm):
class Meta: class Meta:
model = Asset model = Asset
fields = [ fields = [
'hostname', 'ip', 'public_ip', 'port', 'type', 'comment', 'hostname', 'ip', 'public_ip', 'port', 'type', 'comment',
'cluster', 'groups', 'status', 'env', 'is_active', 'cluster', 'groups', 'status', 'env', 'is_active',
'admin_user' 'admin_user', 'labels'
] ]
widgets = { widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}), 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
'cluster': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select cluster')}), 'cluster': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select cluster')}),
'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select admin user')}), 'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select admin user')}),
'port': forms.TextInput() 'labels': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select labels')}),
'port': forms.TextInput(),
} }
help_texts = { help_texts = {
'hostname': '* required', 'hostname': '* required',
...@@ -40,6 +40,10 @@ class AssetCreateForm(forms.ModelForm): ...@@ -40,6 +40,10 @@ class AssetCreateForm(forms.ModelForm):
raise forms.ValidationError(_("You need set a admin user if cluster not have")) raise forms.ValidationError(_("You need set a admin user if cluster not have"))
return self.cleaned_data['admin_user'] return self.cleaned_data['admin_user']
def save(self, commit=True):
print(self.cleaned_data)
return super().save(commit=commit)
class AssetUpdateForm(forms.ModelForm): class AssetUpdateForm(forms.ModelForm):
class Meta: class Meta:
...@@ -47,7 +51,7 @@ class AssetUpdateForm(forms.ModelForm): ...@@ -47,7 +51,7 @@ class AssetUpdateForm(forms.ModelForm):
fields = [ fields = [
'hostname', 'ip', 'port', 'groups', "cluster", 'is_active', 'hostname', 'ip', 'port', 'groups', "cluster", 'is_active',
'type', 'env', 'status', 'public_ip', 'remote_card_ip', 'cabinet_no', 'type', 'env', 'status', 'public_ip', 'remote_card_ip', 'cabinet_no',
'cabinet_pos', 'number', 'comment', 'admin_user', 'cabinet_pos', 'number', 'comment', 'admin_user', 'labels'
] ]
widgets = { widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}), 'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}),
...@@ -68,13 +72,15 @@ class AssetUpdateForm(forms.ModelForm): ...@@ -68,13 +72,15 @@ class AssetUpdateForm(forms.ModelForm):
raise forms.ValidationError(_("You need set a admin user if cluster not have")) raise forms.ValidationError(_("You need set a admin user if cluster not have"))
return self.cleaned_data['admin_user'] return self.cleaned_data['admin_user']
def save(self, commit=True):
print(self.cleaned_data)
return super().save(commit=commit)
class AssetBulkUpdateForm(forms.ModelForm): class AssetBulkUpdateForm(forms.ModelForm):
assets = forms.ModelMultipleChoiceField( assets = forms.ModelMultipleChoiceField(
required=True, required=True, help_text='* required',
help_text='* required', label=_('Select assets'), queryset=Asset.objects.all(),
label=_('Select assets'),
queryset=Asset.objects.all(),
widget=forms.SelectMultiple( widget=forms.SelectMultiple(
attrs={ attrs={
'class': 'select2', 'class': 'select2',
...@@ -83,10 +89,7 @@ class AssetBulkUpdateForm(forms.ModelForm): ...@@ -83,10 +89,7 @@ class AssetBulkUpdateForm(forms.ModelForm):
) )
) )
port = forms.IntegerField( port = forms.IntegerField(
label=_('Port'), label=_('Port'), required=False, min_value=1, max_value=65535,
required=False,
min_value=1,
max_value=65535,
) )
class Meta: class Meta:
...@@ -96,7 +99,9 @@ class AssetBulkUpdateForm(forms.ModelForm): ...@@ -96,7 +99,9 @@ class AssetBulkUpdateForm(forms.ModelForm):
'type', 'env', 'type', 'env',
] ]
widgets = { widgets = {
'groups': forms.SelectMultiple(attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}), 'groups': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select asset groups')}
),
} }
def save(self, commit=True): def save(self, commit=True):
...@@ -140,7 +145,7 @@ class AssetGroupForm(forms.ModelForm): ...@@ -140,7 +145,7 @@ class AssetGroupForm(forms.ModelForm):
def save(self, commit=True): def save(self, commit=True):
group = super().save(commit=commit) group = super().save(commit=commit)
assets= self.cleaned_data['assets'] assets = self.cleaned_data['assets']
group.assets.set(assets) group.assets.set(assets)
return group return group
...@@ -377,3 +382,22 @@ class SystemUserAuthForm(forms.Form): ...@@ -377,3 +382,22 @@ class SystemUserAuthForm(forms.Form):
class FileForm(forms.Form): class FileForm(forms.Form):
file = forms.FileField() file = forms.FileField()
class LabelForm(forms.ModelForm):
assets = forms.ModelMultipleChoiceField(
queryset=Asset.objects.all(), label=_('Asset'), required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
)
)
class Meta:
model = Label
fields = ['name', 'value', 'assets']
def save(self, commit=True):
label = super().save(commit=commit)
assets = self.cleaned_data['assets']
label.assets.set(assets)
return label
...@@ -23,8 +23,15 @@ class Label(models.Model): ...@@ -23,8 +23,15 @@ class Label(models.Model):
auto_now_add=True, null=True, blank=True, verbose_name=_('Date created') auto_now_add=True, null=True, blank=True, verbose_name=_('Date created')
) )
@classmethod
def get_queryset_group_by_name(cls):
names = cls.objects.values_list('name', flat=True)
for name in names:
yield name, cls.objects.filter(name=name)
def __str__(self): def __str__(self):
return "{}:{}".format(self.name, self.value) return "{}:{}".format(self.name, self.value)
class Meta: class Meta:
db_table = "assets_label" db_table = "assets_label"
unique_together = ('name', 'value')
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
{% load static %} {% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load i18n %} {% load i18n %}
{% load asset_tags %}
{% load common_tags %}
{% block form %} {% block form %}
<form action="" method="post" class="form-horizontal"> <form action="" method="post" class="form-horizontal">
...@@ -28,12 +30,32 @@ ...@@ -28,12 +30,32 @@
<h3>{% trans 'Group' %}</h3> <h3>{% trans 'Group' %}</h3>
{% bootstrap_field form.groups layout="horizontal" %} {% bootstrap_field form.groups layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Labels' %}</h3>
<div class="form-group">
<label for="{{ form.labels.id_for_label }}" class="col-md-2 control-label">{% trans 'Labels' %}</label>
<div class="col-md-9">
<select name="labels" class="select2" data-placeholder="Select labels" style="width: 100%" multiple="" tabindex="4" id="{{ form.labels.id_for_label }}">
{% for name, labels in form.labels.field.queryset|group_labels %}
<optgroup label="{{ name }}">
{% for label in labels %}
{% if label in form.labels.initial %}
<option value="{{ label.id }}" selected>{{ label.name }}:{{ label.value }}</option>
{% else %}
<option value="{{ label.id }}">{{ label.name }}:{{ label.value }}</option>
{% endif %}
{% endfor %}
</optgroup>
{% endfor %}
</select>
</div>
</div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>
{% bootstrap_field form.comment layout="horizontal" %} {% bootstrap_field form.comment layout="horizontal" %}
{% bootstrap_field form.is_active layout="horizontal" %} {% bootstrap_field form.is_active layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
{% load static %} {% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% load i18n %} {% load i18n %}
{% load asset_tags %}
{% load common_tags %}
{% block custom_head_css_js_create %} {% block custom_head_css_js_create %}
<link href="{% static "css/plugins/inputTags.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/inputTags.css" %}" rel="stylesheet">
...@@ -33,6 +35,27 @@ ...@@ -33,6 +35,27 @@
<h3>{% trans 'Group' %}</h3> <h3>{% trans 'Group' %}</h3>
{% bootstrap_field form.groups layout="horizontal" %} {% bootstrap_field form.groups layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Labels' %}</h3>
<div class="form-group">
<label for="{{ form.labels.id_for_label }}" class="col-md-2 control-label">{% trans 'Labels' %}</label>
<div class="col-md-9">
<select name="labels" class="select2 labels" data-placeholder="Select labels" style="width: 100%" multiple="" tabindex="4" id="{{ form.labels.id_for_label }}">
{% for name, labels in form.labels.field.queryset|group_labels %}
<optgroup label="{{ name }}">
{% for label in labels %}
{% if label in form.labels.initial %}
<option value="{{ label.id }}" selected>{{ label.value }}</option>
{% else %}
<option value="{{ label.id }}">{{ label.value }}</option>
{% endif %}
{% endfor %}
</optgroup>
{% endfor %}
</select>
</div>
</div>
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Configuration' %}</h3> <h3>{% trans 'Configuration' %}</h3>
{% bootstrap_field form.number layout="horizontal" %} {% bootstrap_field form.number layout="horizontal" %}
...@@ -62,13 +85,17 @@ ...@@ -62,13 +85,17 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
function format(item) {
var group = item.element.parentElement.label;
return group + ':' + item.text;
}
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2({ $('.select2').select2({
allowClear: true allowClear: true
}); });
$("#tags").select2({ $(".labels").select2({
tags: true, allowClear: true,
maximumSelectionLength: 8 //最多能够选择的个数 templateSelection: format
}); });
}) })
</script> </script>
......
{% extends '_base_create_update.html' %}
{% load static %}
{% load bootstrap3 %}
{% load i18n %}
{% block form %}
<form id="groupForm" method="post" class="form-horizontal">
{% csrf_token %}
{% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.value layout="horizontal" %}
{% bootstrap_field form.assets layout="horizontal" %}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
<button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
</div>
</div>
</form>
{% endblock %}
{% block custom_foot_js %}
<script type="text/javascript">
$(document).ready(function () {
$('.select2').select2({
closeOnSelect: false
});
});
</script>
{% endblock %}
\ No newline at end of file
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
{% block table_search %}{% endblock %} {% block table_search %}{% endblock %}
{% block table_container %} {% block table_container %}
<div class="uc pull-left m-r-5"> <div class="uc pull-left m-r-5">
<a href="" class="btn btn-sm btn-primary"> {% trans "Create label" %} </a> <a href="{% url 'assets:label-create' %}" class="btn btn-sm btn-primary"> {% trans "Create label" %} </a>
</div> </div>
<table class="table table-striped table-bordered table-hover " id="label_list_table" > <table class="table table-striped table-bordered table-hover " id="label_list_table" >
<thead> <thead>
......
from collections import defaultdict
from django import template from django import template
from django.utils import timezone
from django.conf import settings
register = template.Library() register = template.Library()
@register.filter
def group_labels(queryset):
grouped = defaultdict(list)
for label in queryset:
grouped[label.name].append(label)
return [(name, labels) for name, labels in grouped.items()]
...@@ -54,5 +54,6 @@ urlpatterns = [ ...@@ -54,5 +54,6 @@ urlpatterns = [
# name='system-user-asset-group'), # name='system-user-asset-group'),
url(r'^label/$', views.LabelListView.as_view(), name='label-list'), url(r'^label/$', views.LabelListView.as_view(), name='label-list'),
url(r'^label/create/$', views.LabelCreateView.as_view(), name='label-create'),
] ]
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.views.generic import ListView, TemplateView, CreateView, \ from django.views.generic import TemplateView, CreateView, \
UpdateView, DeleteView, DetailView UpdateView, DeleteView, DetailView
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.urls import reverse_lazy
from common.mixins import AdminUserRequiredMixin from common.mixins import AdminUserRequiredMixin
from common.const import create_success_msg
from ..forms import LabelForm
__all__ = ( __all__ = (
...@@ -28,7 +30,18 @@ class LabelListView(AdminUserRequiredMixin, TemplateView): ...@@ -28,7 +30,18 @@ class LabelListView(AdminUserRequiredMixin, TemplateView):
class LabelCreateView(AdminUserRequiredMixin, CreateView): class LabelCreateView(AdminUserRequiredMixin, CreateView):
pass template_name = 'assets/label_create_update.html'
form_class = LabelForm
success_url = reverse_lazy('assets:label-list')
success_message = create_success_msg
def get_context_data(self, **kwargs):
context = {
'app': _('Assets'),
'action': _('Create label'),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
class LabelUpdateView(AdminUserRequiredMixin, UpdateView): class LabelUpdateView(AdminUserRequiredMixin, UpdateView):
......
...@@ -92,3 +92,8 @@ def is_bool_field(field): ...@@ -92,3 +92,8 @@ def is_bool_field(field):
return True return True
else: else:
return False return False
@register.filter
def to_dict(data):
return dict(data)
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
<li id="cluster"><a href="{% url 'assets:cluster-list' %}">{% trans 'Cluster' %}</a></li> <li id="cluster"><a href="{% url 'assets:cluster-list' %}">{% trans 'Cluster' %}</a></li>
<li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li> <li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li> <li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
<li id="system-user"><a href="{% url 'assets:label-list' %}">{% trans 'Label' %}</a></li> <li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Label' %}</a></li>
</ul> </ul>
</li> </li>
<li id="perms"> <li id="perms">
...@@ -54,8 +54,6 @@ ...@@ -54,8 +54,6 @@
<li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Command' %}</a></li> <li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Command' %}</a></li>
</ul> </ul>
</li> </li>
{#<li id="">#} {#<li id="">#}
{# <a href="#">#} {# <a href="#">#}
{# <i class="fa fa-download"></i> <span class="nav-label">{% trans 'File' %}</span><span class="fa arrow"></span>#} {# <i class="fa fa-download"></i> <span class="nav-label">{% trans 'File' %}</span><span class="fa arrow"></span>#}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment