2017-12-01 1 views
3

플라스크 기능에서 팝업 창을 시작할 수있는 방법이 있습니까?팝업 모달 창을 통한 인수가있는 Flask-admin 일괄 처리

나는 flask-admin + flask-sqlalchemy 어플입니다. DB의 테이블에는 값이있는 foo 필드가 있습니다. UserAdmin보기가 있는데 외부 인수로 batch action을 만들려고합니다.

  • 이 (가) 새로운 사용자 정의 값을 가진 요소
  • 내가 원하는 방식의 각각에 대한 몇 가지 내 DB의 요소와
  • 대체 이전 foo 값을 선택에 즉 내가 원하는 이 새 값을 받으려면 모달 창입니다.

그래서 모델은 다음과 같습니다

class User(db.Model): 
    # some code 
    foo = Column(Integer) 
    def change_foo(self, new_foo): 
     self.foo = int(new_foo) 
     db.session.commit() 
     return True 

class UserAdmin(sqla.ModelView): 
    # some code 
    @action('change_foo', 'Change foo', 'Are you sure you want to change foo for selected users?') 
    def action_change_foo(self, ids): 
     query = tools.get_query_for_ids(self.get_query(), self.model, ids) 
     count = 0 
     # TODO: pop-up modal window here to get new_foo_value 
     # new_foo_value = ??? 
     for user in query.all(): 
      if user.change_foo(new_foo_value): 
       count += 1 
     flash(ngettext('Foo was successfully changed.', 
         '%(count)s foos were successfully changed.', 
         count, count=count)) 
    except Exception as e: 
     flash(ngettext('Failed to change foo for selected users. %(error)s', error=str(e)), 'error') 

나는 전체 접근 방식이 최적이 아닌 것을 인정, 그래서 하나 더와 조언을 기꺼이 것입니다.

«Batch Editing in Flask-Admin»(아직 답변이 없음) 및«Flask-Admin batch action with form»(WTF 양식을 사용하는 일부 해결 방법이 있음).

+0

@pjcunningham 정말 멋진 해결책을 가져 주셔서 감사합니다. 나는 그것을 나의 응용을 위해 적용했고 그것은 잘 작동한다. 필자가 변경 한 것은'print change_form.errors' 대신'flash()'메시지 뿐이었고 이후에는'code = 307'을 삭제했습니다. (그럼에도 불구하고 그것은 여전히 ​​나를 위해 마술처럼 보입니다.) –

답변

4

여기에이 방법이 있습니다. Github에 완전 독립형 예제 인 flask-admin-modal을 넣었습니다.

이 예제에서 SQLite 데이터베이스 모델은 이름 (문자열) 및 비용 (정수) 속성이있는 프로젝트이며 Flask-Admin 목록보기에서 선택된 행의 비용 값을 업데이트합니다. Flask 응용 프로그램이 시작되면 데이터베이스에 임의의 데이터가 채워집니다.

여기 모델의 :

class Project(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(255), nullable=False, unique=True) 
    cost = db.Column(db.Integer(), nullable=False) 

    def __str__(self): 
     return unicode(self).encode('utf-8') 

    def __unicode__(self): 
     return "Name: {name}; Cost : {cost}".format(name=self.name, cost=self.cost) 

는 새로운 비용을 받아 정수 비용 필드 양식을 정의합니다. 이 양식에는 선택된 행 ID를 추적하는 숨겨진 필드도 있습니다.

class ChangeForm(Form): 
    ids = HiddenField() 
    cost = IntegerField(validators=[InputRequired()]) 

프로젝트보기 모델의 목록 템플릿을 재정의하십시오. 우리는 changeModel이라는 ID를 가진 부트 스트랩 모달 형식을 {% block body %}에 삽입 할 수 있도록 {{ super() }}을 먼저 호출해야합니다.

또한 템플릿 변수 (change_model)가 true로 평가되면 모달 폼을 표시하는 jQuery 문서 준비 함수를 추가합니다. 동일한 변수가 모델 본체에서 change_form을 표시하는 데 사용됩니다. 우리는 Flask-Admin lib 매크로 인 render_form을 사용하여 폼을 부트 스트랩 스타일로 렌더링합니다.

조치 매개 변수의 값은 render_form입니다. 이는 프로젝트보기에서 양식의 데이터를 처리 할 수있는 경로입니다. 또한 "닫기"버튼이 링크로 대체되었지만 버튼으로 스타일이 지정되었습니다. 링크는 작업이 시작된 원본 URL (페이지 및 필터 세부 정보 포함)입니다.

{% extends 'admin/model/list.html' %} 

{% block body %} 
    {{ super() }} 

    <div class="modal fade" tabindex="-1" role="dialog" id="changeModal"> 
     <div class="modal-dialog" role="document"> 
     <div class="modal-content"> 
      <div class="modal-header"> 
      <a href="{{ url }}" class="close" aria-label="Close"><span aria-hidden="true">&times;</span></a> 
      <h4 class="modal-title">Change Project Costs</h4> 
      </div> 
      <div class="modal-body"> 
       {% if change_modal %} 
        {{ lib.render_form(change_form, action=url_for('project.update_view', url=url)) }} 
       {% endif %} 
      </div> 
     </div><!-- /.modal-content --> 
     </div><!-- /.modal-dialog --> 
    </div><!-- /.modal --> 
{% endblock body %} 

{% block tail %} 
    {{ super() }} 
    <script> 
     {% if change_modal %} 
      $(document).ready(function(){ 
       $("#changeModal").modal('show'); 
      }); 
     {% endif %} 
    </script> 
{% endblock tail %} 

프로젝트 뷰 클래스는 배치 작업 메서드의 동작을 수정하고 POST 요청을 받아들이는 몇 개의 경로를 정의해야합니다.

@action('change_cost', 'Change Cost', 'Are you sure you want to change Cost for selected projects?') 
def action_change_cost(self, ids): 
    url = get_redirect_target() or self.get_url('.index_view') 
    return redirect(url, code=307) 

직접 배치 행동을 ID를 처리하는 작업을 게시하는 URL을 가져옵니다 대신에,이 URL은 페이지 번호 및 필터 세부 사항이 포함됩니다. 그런 다음 307을 사용하여 목록보기로 다시 리디렉션합니다. 이렇게하면 선택한 행 ID가 본문에서 전달되고 POST 요청이라는 사실이 보장됩니다.

이 리디렉션을 처리 할 수있는 POST 경로를 정의하고 요청 본문 (예 : ChangeForm)에서 ID와 URL을 가져온 다음 숨겨진 ids 양식 필드를 ID의 인코딩 된 목록으로 설정합니다. url, change_formchange_model 변수를 템플릿 args에 추가 한 다음 목록보기를 다시 렌더링하십시오. 이번에는 모달 팝업 폼이보기에 표시됩니다.

@expose('/', methods=['POST']) 
def index(self): 
    if request.method == 'POST': 
     url = get_redirect_target() or self.get_url('.index_view') 
     ids = request.form.getlist('rowid') 
     joined_ids = ','.join(ids) 
     encoded_ids = base64.b64encode(joined_ids) 
     change_form = ChangeForm() 
     change_form.ids.data = encoded_ids 
     self._template_args['url'] = url 
     self._template_args['change_form'] = change_form 
     self._template_args['change_modal'] = True 
     return self.index_view() 

모달 양식의 데이터를 처리하기위한 POST 경로를 정의하십시오. 이것은 표준 양식/데이터베이스 처리이며 완료되면 작업을 시작한 원래 URL로 다시 리디렉션됩니다.

@expose('/update/', methods=['POST']) 
def update_view(self): 
    if request.method == 'POST': 
     url = get_redirect_target() or self.get_url('.index_view') 
     change_form = ChangeForm(request.form) 
     if change_form.validate(): 
      decoded_ids = base64.b64decode(change_form.ids.data) 
      ids = decoded_ids.split(',') 
      cost = change_form.cost.data 
      _update_mappings = [{'id': rowid, 'cost': cost} for rowid in ids] 
      db.session.bulk_update_mappings(Project, _update_mappings) 
      db.session.commit() 
      return redirect(url) 
     else: 
      # Form didn't validate 
      # todo need to display the error message in the pop-up 
      print change_form.errors 
      return redirect(url, code=307) 
+0

해결책을 가져 주셔서 감사합니다. 이것은 더 많은 upvotes받을 가치가있어! – ken

관련 문제